All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 06/12] libata-pmp-prep: implement qc_defer helpers
  2007-07-01 10:26 [PATCHSET 3/4] libata: prep for PMP support, take 4 Tejun Heo
@ 2007-07-01 10:26 ` Tejun Heo
  0 siblings, 0 replies; 15+ messages in thread
From: Tejun Heo @ 2007-07-01 10:26 UTC (permalink / raw)
  To: Jeff Garzik, Alan Cox, linux-ide, Forrest Zhao; +Cc: Tejun Heo

Implement ap->nr_active_links (the number of links with active qcs),
ap->excl_link (pointer to link which can be used by ->qc_defer and is
cleared when a qc with ATA_QCFLAG_CLEAR_EXCL completes), and
ata_link_active().

These can be used by ->qc_defer() to implement proper command
exclusion.  This set of helpers seem enough for both sil24 (ATAPI
exclusion needed) and cmd-switching PMP.

Signed-off-by: Tejun Heo <htejun@gmail.com>
---
 drivers/ata/libata-core.c |   22 ++++++++++++++++++++--
 drivers/ata/libata-eh.c   |    5 +++++
 include/linux/libata.h    |    8 ++++++++
 3 files changed, 33 insertions(+), 2 deletions(-)

diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
index fb5d7e7..17b1734 100644
--- a/drivers/ata/libata-core.c
+++ b/drivers/ata/libata-core.c
@@ -1381,6 +1381,7 @@ unsigned ata_exec_internal_sg(struct ata_device *dev,
 	struct ata_queued_cmd *qc;
 	unsigned int tag, preempted_tag;
 	u32 preempted_sactive, preempted_qc_active;
+	int preempted_nr_active_links;
 	DECLARE_COMPLETION_ONSTACK(wait);
 	unsigned long flags;
 	unsigned int err_mask;
@@ -1419,9 +1420,11 @@ unsigned ata_exec_internal_sg(struct ata_device *dev,
 	preempted_tag = link->active_tag;
 	preempted_sactive = link->sactive;
 	preempted_qc_active = ap->qc_active;
+	preempted_nr_active_links = ap->nr_active_links;
 	link->active_tag = ATA_TAG_POISON;
 	link->sactive = 0;
 	ap->qc_active = 0;
+	ap->nr_active_links = 0;
 
 	/* prepare & issue qc */
 	qc->tf = *tf;
@@ -1500,6 +1503,7 @@ unsigned ata_exec_internal_sg(struct ata_device *dev,
 	link->active_tag = preempted_tag;
 	link->sactive = preempted_sactive;
 	ap->qc_active = preempted_qc_active;
+	ap->nr_active_links = preempted_nr_active_links;
 
 	/* XXX - Some LLDDs (sata_mv) disable port on command failure.
 	 * Until those drivers are fixed, we detect the condition
@@ -5208,10 +5212,19 @@ void __ata_qc_complete(struct ata_queued_cmd *qc)
 		ata_sg_clean(qc);
 
 	/* command should be marked inactive atomically with qc completion */
-	if (qc->tf.protocol == ATA_PROT_NCQ)
+	if (qc->tf.protocol == ATA_PROT_NCQ) {
 		link->sactive &= ~(1 << qc->tag);
-	else
+		if (!link->sactive)
+			ap->nr_active_links--;
+	} else {
 		link->active_tag = ATA_TAG_POISON;
+		ap->nr_active_links--;
+	}
+
+	/* clear exclusive status */
+	if (unlikely(qc->flags & ATA_QCFLAG_CLEAR_EXCL &&
+		     ap->excl_link == link))
+		ap->excl_link = NULL;
 
 	/* atapi: mark qc as inactive to prevent the interrupt handler
 	 * from completing the command twice later, before the error handler
@@ -5390,9 +5403,14 @@ void ata_qc_issue(struct ata_queued_cmd *qc)
 
 	if (qc->tf.protocol == ATA_PROT_NCQ) {
 		WARN_ON(link->sactive & (1 << qc->tag));
+
+		if (!link->sactive)
+			ap->nr_active_links++;
 		link->sactive |= 1 << qc->tag;
 	} else {
 		WARN_ON(link->sactive);
+
+		ap->nr_active_links++;
 		link->active_tag = qc->tag;
 	}
 
diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c
index ec0b90b..3779067 100644
--- a/drivers/ata/libata-eh.c
+++ b/drivers/ata/libata-eh.c
@@ -316,6 +316,7 @@ void ata_scsi_error(struct Scsi_Host *host)
 
 		ap->pflags |= ATA_PFLAG_EH_IN_PROGRESS;
 		ap->pflags &= ~ATA_PFLAG_EH_PENDING;
+		ap->excl_link = NULL;	/* don't maintain exclusion over EH */
 
 		spin_unlock_irqrestore(ap->lock, flags);
 
@@ -2336,6 +2337,10 @@ void ata_eh_finish(struct ata_port *ap)
 			}
 		}
 	}
+
+	/* make sure nr_active_links is zero after EH */
+	WARN_ON(ap->nr_active_links);
+	ap->nr_active_links = 0;
 }
 
 /**
diff --git a/include/linux/libata.h b/include/linux/libata.h
index fa332e0..349180a 100644
--- a/include/linux/libata.h
+++ b/include/linux/libata.h
@@ -215,6 +215,7 @@ enum {
 	ATA_QCFLAG_DMAMAP	= ATA_QCFLAG_SG | ATA_QCFLAG_SINGLE,
 	ATA_QCFLAG_IO		= (1 << 3), /* standard IO command */
 	ATA_QCFLAG_RESULT_TF	= (1 << 4), /* result TF requested */
+	ATA_QCFLAG_CLEAR_EXCL	= (1 << 5), /* clear excl_link on completion */
 
 	ATA_QCFLAG_FAILED	= (1 << 16), /* cmd failed and is owned by EH */
 	ATA_QCFLAG_SENSE_VALID	= (1 << 17), /* sense data valid */
@@ -576,11 +577,13 @@ struct ata_port {
 	struct ata_queued_cmd	qcmd[ATA_MAX_QUEUE];
 	unsigned long		qc_allocated;
 	unsigned int		qc_active;
+	int			nr_active_links; /* #links with active qcs */
 
 	struct ata_link		link;	/* host default link */
 
 	int			nr_pmp_links;	/* nr of available PMP links */
 	struct ata_link		*pmp_link;	/* array of PMP links */
+	struct ata_link		*excl_link;	/* for PMP qc exclusion */
 
 	struct ata_port_stats	stats;
 	struct ata_host		*host;
@@ -1093,6 +1096,11 @@ static inline int ata_link_max_devices(const struct ata_link *link)
 	return 1;
 }
 
+static inline int ata_link_active(struct ata_link *link)
+{
+	return ata_tag_valid(link->active_tag) || link->sactive;
+}
+
 static inline struct ata_link *ata_port_first_link(struct ata_port *ap)
 {
 	if (ap->nr_pmp_links)
-- 
1.5.0.3



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

* [PATCHSET 1/2] libata: prep for PMP support, take 6
@ 2007-09-23  4:14 Tejun Heo
  2007-09-23  4:14 ` [PATCH 01/12] libata: misc updates for AN Tejun Heo
                   ` (11 more replies)
  0 siblings, 12 replies; 15+ messages in thread
From: Tejun Heo @ 2007-09-23  4:14 UTC (permalink / raw)
  To: jeff, alan, linux-ide, htejun

Hello, all.

This is the sixth take of libata-pmp-prep patchset.  This patchset
contains 12 patches implementing various stuff needed for PMP.

#01-04	: various small stuff
#05-06	: qc_defer mechanism implementation
#07-11	: link/device quirks and EH fast-fail path - things needed to
	  handle quirky PMP fan-out links / pseudo devices.
#12	: AN demultiplexer - sata_async_notification()

Change from the last take[L] are...

* Updated to handle both ATAPI AN and PMP AN as ATAPI AN has been
  merged inbetween.  This is done by adding new patch #01 and updating
  patch #12.

This patchset is against

  libata-dev#upstream (b56feca2ede0dd11a8409743ff14cf14cbcf5062)
  + [1] sata_sil24-fix-IRQ-clearing-race-when-PCIX_IRQ_WOC-is-used

Thanks.

--
tejun

[L] http://thread.gmane.org/gmane.linux.ide/20857
[1] http://article.gmane.org/gmane.linux.ide/22918 



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

* [PATCH 01/12] libata: misc updates for AN
  2007-09-23  4:14 [PATCHSET 1/2] libata: prep for PMP support, take 6 Tejun Heo
@ 2007-09-23  4:14 ` Tejun Heo
  2007-09-26  2:27   ` Jeff Garzik
  2007-09-23  4:14 ` [PATCH 08/12] libata-pmp-prep: implement ATA_LFLAG_NO_RETRY Tejun Heo
                   ` (10 subsequent siblings)
  11 siblings, 1 reply; 15+ messages in thread
From: Tejun Heo @ 2007-09-23  4:14 UTC (permalink / raw)
  To: jeff, alan, linux-ide; +Cc: Tejun Heo

Update AN support in preparation of PMP support.

* s/ata_id_has_AN/ata_id_has_atapi_AN/
* add AN enabled reporting during configuration
* add err_mask to AN configuration failure reporting
* update LOCKING comment for ata_scsi_media_change_notify()
* check whether ATA dev is attached to SCSI dev ata_scsi_media_change_notify()
* set ATA_FLAG_AN in ahci and sata_sil24

Signed-off-by: Tejun Heo <htejun@gmail.com>
Cc: Kriten Carlson Accardi <kristen.c.accardi@intel.com>
---
 drivers/ata/ahci.c        |    2 +-
 drivers/ata/libata-core.c |   24 ++++++++++++++----------
 drivers/ata/libata-scsi.c |    7 ++++---
 drivers/ata/sata_sil24.c  |   13 +++++++++++--
 include/linux/ata.h       |    2 +-
 5 files changed, 31 insertions(+), 17 deletions(-)

diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c
index b721569..0a6b694 100644
--- a/drivers/ata/ahci.c
+++ b/drivers/ata/ahci.c
@@ -177,7 +177,7 @@ enum {
 
 	AHCI_FLAG_COMMON		= ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
 					  ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA |
-					  ATA_FLAG_ACPI_SATA,
+					  ATA_FLAG_ACPI_SATA | ATA_FLAG_AN,
 	AHCI_LFLAG_COMMON		= ATA_LFLAG_SKIP_D2H_BSY,
 };
 
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
index 2116f27..63274b6 100644
--- a/drivers/ata/libata-core.c
+++ b/drivers/ata/libata-core.c
@@ -2012,7 +2012,8 @@ int ata_dev_configure(struct ata_device *dev)
 
 	/* ATAPI-specific feature tests */
 	else if (dev->class == ATA_DEV_ATAPI) {
-		char *cdb_intr_string = "";
+		const char *cdb_intr_string = "";
+		const char *atapi_an_string = "";
 
 		rc = atapi_cdb_len(id);
 		if ((rc < 12) || (rc > ATAPI_CDB_LEN)) {
@@ -2028,16 +2029,19 @@ int ata_dev_configure(struct ata_device *dev)
 		 * check to see if this ATAPI device supports
 		 * Asynchronous Notification
 		 */
-		if ((ap->flags & ATA_FLAG_AN) && ata_id_has_AN(id)) {
-			int err;
+		if ((ap->flags & ATA_FLAG_AN) && ata_id_has_atapi_AN(id)) {
+			unsigned int err_mask;
+
 			/* issue SET feature command to turn this on */
-			err = ata_dev_set_AN(dev, SETFEATURES_SATA_ENABLE);
-			if (err)
+			err_mask = ata_dev_set_AN(dev, SETFEATURES_SATA_ENABLE);
+			if (err_mask)
 				ata_dev_printk(dev, KERN_ERR,
-						"unable to set AN, err %x\n",
-						err);
-			else
+					"failed to enable ATAPI AN "
+					"(err_mask=0x%x)\n", err_mask);
+			else {
 				dev->flags |= ATA_DFLAG_AN;
+				atapi_an_string = ", ATAPI AN";
+			}
 		}
 
 		if (ata_id_cdb_intr(dev->id)) {
@@ -2048,10 +2052,10 @@ int ata_dev_configure(struct ata_device *dev)
 		/* print device info to dmesg */
 		if (ata_msg_drv(ap) && print_info)
 			ata_dev_printk(dev, KERN_INFO,
-				       "ATAPI: %s, %s, max %s%s\n",
+				       "ATAPI: %s, %s, max %s%s%s\n",
 				       modelbuf, fwrevbuf,
 				       ata_mode_string(xfer_mask),
-				       cdb_intr_string);
+				       cdb_intr_string, atapi_an_string);
 	}
 
 	/* determine max_sectors */
diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c
index 35284b3..518d475 100644
--- a/drivers/ata/libata-scsi.c
+++ b/drivers/ata/libata-scsi.c
@@ -3161,12 +3161,13 @@ static void ata_scsi_handle_link_detach(struct ata_link *link)
  *	event.
  *
  * 	LOCKING:
- * 	interrupt context, may not sleep.
+ * 	spin_lock_irqsave(host lock)
  */
-void ata_scsi_media_change_notify(struct ata_device *atadev)
+void ata_scsi_media_change_notify(struct ata_device *dev)
 {
 #ifdef OTHER_AN_PATCHES_HAVE_BEEN_APPLIED
-	scsi_device_event_notify(atadev->sdev, SDEV_MEDIA_CHANGE);
+	if (dev->sdev)
+		scsi_device_event_notify(dev->sdev, SDEV_MEDIA_CHANGE);
 #endif
 }
 EXPORT_SYMBOL_GPL(ata_scsi_media_change_notify);
diff --git a/drivers/ata/sata_sil24.c b/drivers/ata/sata_sil24.c
index 3dcb223..d9c010a 100644
--- a/drivers/ata/sata_sil24.c
+++ b/drivers/ata/sata_sil24.c
@@ -168,7 +168,7 @@ enum {
 
 	DEF_PORT_IRQ		= PORT_IRQ_COMPLETE | PORT_IRQ_ERROR |
 				  PORT_IRQ_PHYRDY_CHG | PORT_IRQ_DEV_XCHG |
-				  PORT_IRQ_UNK_FIS,
+				  PORT_IRQ_UNK_FIS | PORT_IRQ_SDB_NOTIFY,
 
 	/* bits[27:16] are unmasked (raw) */
 	PORT_IRQ_RAW_SHIFT	= 16,
@@ -237,7 +237,8 @@ enum {
 	/* host flags */
 	SIL24_COMMON_FLAGS	= ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
 				  ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA |
-				  ATA_FLAG_NCQ | ATA_FLAG_ACPI_SATA,
+				  ATA_FLAG_NCQ | ATA_FLAG_ACPI_SATA |
+				  ATA_FLAG_AN,
 	SIL24_COMMON_LFLAGS	= ATA_LFLAG_SKIP_D2H_BSY,
 	SIL24_FLAG_PCIX_IRQ_WOC	= (1 << 24), /* IRQ loss errata on PCI-X */
 
@@ -818,6 +819,14 @@ static void sil24_error_intr(struct ata_port *ap)
 
 	ata_ehi_push_desc(ehi, "irq_stat 0x%08x", irq_stat);
 
+	if (irq_stat & PORT_IRQ_SDB_NOTIFY) {
+		struct ata_device *dev = ap->link.device;
+
+		ata_ehi_push_desc(ehi, "SDB notify");
+		if (dev->flags & ATA_DFLAG_AN)
+			ata_scsi_media_change_notify(dev);
+	}
+
 	if (irq_stat & (PORT_IRQ_PHYRDY_CHG | PORT_IRQ_DEV_XCHG)) {
 		ata_ehi_hotplugged(ehi);
 		ata_ehi_push_desc(ehi, "%s",
diff --git a/include/linux/ata.h b/include/linux/ata.h
index a749f00..21f00a0 100644
--- a/include/linux/ata.h
+++ b/include/linux/ata.h
@@ -353,7 +353,7 @@ struct ata_taskfile {
 #define ata_id_queue_depth(id)	(((id)[75] & 0x1f) + 1)
 #define ata_id_removeable(id)	((id)[0] & (1 << 7))
 #define ata_id_has_dword_io(id)	((id)[48] & (1 << 0))
-#define ata_id_has_AN(id)	\
+#define ata_id_has_atapi_AN(id)	\
 	( (((id)[76] != 0x0000) && ((id)[76] != 0xffff)) && \
 	  ((id)[78] & (1 << 5)) )
 #define ata_id_iordy_disable(id) ((id)[49] & (1 << 10))
-- 
1.5.0.3



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

* [PATCH 02/12] libata-pmp-prep: add PMP related constants, fields, ops and update helpers
  2007-09-23  4:14 [PATCHSET 1/2] libata: prep for PMP support, take 6 Tejun Heo
                   ` (6 preceding siblings ...)
  2007-09-23  4:14 ` [PATCH 03/12] libata-pmp-prep: add @new_class to ata_dev_revalidate() Tejun Heo
@ 2007-09-23  4:14 ` Tejun Heo
  2007-09-23  4:14 ` [PATCH 12/12] libata-pmp-prep: implement sata_async_notification() Tejun Heo
                   ` (3 subsequent siblings)
  11 siblings, 0 replies; 15+ messages in thread
From: Tejun Heo @ 2007-09-23  4:14 UTC (permalink / raw)
  To: jeff, alan, linux-ide; +Cc: Tejun Heo

Add PMP related constants, fields and ops.  Also, update
ata_class_enabled/disabled() such that PMP classes are considered.

Signed-off-by: Tejun Heo <htejun@gmail.com>
---
 include/linux/libata.h |   30 ++++++++++++++++++++++++++----
 1 files changed, 26 insertions(+), 4 deletions(-)

diff --git a/include/linux/libata.h b/include/linux/libata.h
index 3ab2196..c3820f1 100644
--- a/include/linux/libata.h
+++ b/include/linux/libata.h
@@ -155,7 +155,11 @@ enum {
 	ATA_DEV_ATA_UNSUP	= 2,	/* ATA device (unsupported) */
 	ATA_DEV_ATAPI		= 3,	/* ATAPI device */
 	ATA_DEV_ATAPI_UNSUP	= 4,	/* ATAPI device (unsupported) */
-	ATA_DEV_NONE		= 5,	/* no device */
+	ATA_DEV_PMP		= 5,	/* SATA port multiplier */
+	ATA_DEV_PMP_UNSUP	= 6,	/* SATA port multiplier (unsupported) */
+	ATA_DEV_SEMB		= 7,	/* SEMB */
+	ATA_DEV_SEMB_UNSUP	= 8,	/* SEMB (unsupported) */
+	ATA_DEV_NONE		= 9,	/* no device */
 
 	/* struct ata_link flags */
 	ATA_LFLAG_HRST_TO_RESUME = (1 << 0), /* hardreset to resume link */
@@ -181,6 +185,7 @@ enum {
 	ATA_FLAG_NO_IORDY	= (1 << 16), /* controller lacks iordy */
 	ATA_FLAG_ACPI_SATA	= (1 << 17), /* need native SATA ACPI layout */
 	ATA_FLAG_AN		= (1 << 18), /* controller supports AN */
+	ATA_FLAG_PMP		= (1 << 19), /* controller supports PMP */
 
 	/* The following flag belongs to ap->pflags but is kept in
 	 * ap->flags because it's referenced in many LLDs and will be
@@ -299,6 +304,10 @@ enum {
 	/* how hard are we gonna try to probe/recover devices */
 	ATA_PROBE_MAX_TRIES	= 3,
 	ATA_EH_DEV_TRIES	= 3,
+	ATA_EH_PMP_TRIES	= 5,
+	ATA_EH_PMP_LINK_TRIES	= 3,
+
+	SATA_PMP_SCR_TIMEOUT	= 250,
 
 	/* Horkage types. May be set by libata or controller on drives
 	   (some horkage may be drive/controller pair dependant */
@@ -450,7 +459,12 @@ struct ata_device {
 	/* n_sector is used as CLEAR_OFFSET, read comment above CLEAR_OFFSET */
 	u64			n_sectors;	/* size of device, if ATA */
 	unsigned int		class;		/* ATA_DEV_xxx */
-	u16			id[ATA_ID_WORDS]; /* IDENTIFY xxx DEVICE data */
+
+	union {
+		u16		id[ATA_ID_WORDS]; /* IDENTIFY xxx DEVICE data */
+		u32		gscr[SATA_PMP_GSCR_DWORDS]; /* PMP GSCR block */
+	};
+
 	u8			pio_mode;
 	u8			dma_mode;
 	u8			xfer_mode;
@@ -628,6 +642,12 @@ struct ata_port_operations {
 	void (*qc_prep) (struct ata_queued_cmd *qc);
 	unsigned int (*qc_issue) (struct ata_queued_cmd *qc);
 
+	/* port multiplier */
+	void (*pmp_attach) (struct ata_port *ap);
+	void (*pmp_detach) (struct ata_port *ap);
+	int (*pmp_read) (struct ata_device *dev, int pmp, int reg, u32 *r_val);
+	int (*pmp_write) (struct ata_device *dev, int pmp, int reg, u32 val);
+
 	/* Error handlers.  ->error_handler overrides ->eng_timeout and
 	 * indicates that new-style EH is in place.
 	 */
@@ -1033,12 +1053,14 @@ static inline unsigned int ata_tag_internal(unsigned int tag)
  */
 static inline unsigned int ata_class_enabled(unsigned int class)
 {
-	return class == ATA_DEV_ATA || class == ATA_DEV_ATAPI;
+	return class == ATA_DEV_ATA || class == ATA_DEV_ATAPI ||
+		class == ATA_DEV_PMP || class == ATA_DEV_SEMB;
 }
 
 static inline unsigned int ata_class_disabled(unsigned int class)
 {
-	return class == ATA_DEV_ATA_UNSUP || class == ATA_DEV_ATAPI_UNSUP;
+	return class == ATA_DEV_ATA_UNSUP || class == ATA_DEV_ATAPI_UNSUP ||
+		class == ATA_DEV_PMP_UNSUP || class == ATA_DEV_SEMB_UNSUP;
 }
 
 static inline unsigned int ata_class_absent(unsigned int class)
-- 
1.5.0.3



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

* [PATCH 03/12] libata-pmp-prep: add @new_class to ata_dev_revalidate()
  2007-09-23  4:14 [PATCHSET 1/2] libata: prep for PMP support, take 6 Tejun Heo
                   ` (5 preceding siblings ...)
  2007-09-23  4:14 ` [PATCH 06/12] libata-pmp-prep: implement qc_defer helpers Tejun Heo
@ 2007-09-23  4:14 ` Tejun Heo
  2007-09-23  4:14 ` [PATCH 02/12] libata-pmp-prep: add PMP related constants, fields, ops and update helpers Tejun Heo
                   ` (4 subsequent siblings)
  11 siblings, 0 replies; 15+ messages in thread
From: Tejun Heo @ 2007-09-23  4:14 UTC (permalink / raw)
  To: jeff, alan, linux-ide; +Cc: Tejun Heo

Consider newly found class code while revalidating.  PMP resetting
always results in valid class code and issuing PMP commands to
ATA/ATAPI device isn't very attractive.  Add @new_class to
ata_dev_revalidate() and check class code for revalidation.

Signed-off-by: Tejun Heo <htejun@gmail.com>
---
 drivers/ata/libata-core.c |   15 +++++++++++++--
 drivers/ata/libata-eh.c   |    3 ++-
 drivers/ata/libata.h      |    3 ++-
 3 files changed, 17 insertions(+), 4 deletions(-)

diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
index 63274b6..bcddc93 100644
--- a/drivers/ata/libata-core.c
+++ b/drivers/ata/libata-core.c
@@ -2848,7 +2848,7 @@ static int ata_dev_set_mode(struct ata_device *dev)
 	}
 
 	ehc->i.flags |= ATA_EHI_POST_SETMODE;
-	rc = ata_dev_revalidate(dev, 0);
+	rc = ata_dev_revalidate(dev, ATA_DEV_UNKNOWN, 0);
 	ehc->i.flags &= ~ATA_EHI_POST_SETMODE;
 	if (rc)
 		return rc;
@@ -3752,6 +3752,7 @@ int ata_dev_reread_id(struct ata_device *dev, unsigned int readid_flags)
 /**
  *	ata_dev_revalidate - Revalidate ATA device
  *	@dev: device to revalidate
+ *	@new_class: new class code
  *	@readid_flags: read ID flags
  *
  *	Re-read IDENTIFY page, make sure @dev is still attached to the
@@ -3763,7 +3764,8 @@ int ata_dev_reread_id(struct ata_device *dev, unsigned int readid_flags)
  *	RETURNS:
  *	0 on success, negative errno otherwise
  */
-int ata_dev_revalidate(struct ata_device *dev, unsigned int readid_flags)
+int ata_dev_revalidate(struct ata_device *dev, unsigned int new_class,
+		       unsigned int readid_flags)
 {
 	u64 n_sectors = dev->n_sectors;
 	int rc;
@@ -3771,6 +3773,15 @@ int ata_dev_revalidate(struct ata_device *dev, unsigned int readid_flags)
 	if (!ata_dev_enabled(dev))
 		return -ENODEV;
 
+	/* fail early if !ATA && !ATAPI to avoid issuing [P]IDENTIFY to PMP */
+	if (ata_class_enabled(new_class) &&
+	    new_class != ATA_DEV_ATA && new_class != ATA_DEV_ATAPI) {
+		ata_dev_printk(dev, KERN_INFO, "class mismatch %u != %u\n",
+			       dev->class, new_class);
+		rc = -ENODEV;
+		goto fail;
+	}
+
 	/* re-read ID */
 	rc = ata_dev_reread_id(dev, readid_flags);
 	if (rc)
diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c
index daa2f74..eb087bb 100644
--- a/drivers/ata/libata-eh.c
+++ b/drivers/ata/libata-eh.c
@@ -2104,7 +2104,8 @@ static int ata_eh_revalidate_and_attach(struct ata_link *link,
 			}
 
 			ata_eh_about_to_do(link, dev, ATA_EH_REVALIDATE);
-			rc = ata_dev_revalidate(dev, readid_flags);
+			rc = ata_dev_revalidate(dev, ehc->classes[dev->devno],
+						readid_flags);
 			if (rc)
 				goto err;
 
diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h
index 11f64a4..3457048 100644
--- a/drivers/ata/libata.h
+++ b/drivers/ata/libata.h
@@ -77,7 +77,8 @@ extern unsigned int ata_do_simple_cmd(struct ata_device *dev, u8 cmd);
 extern int ata_dev_read_id(struct ata_device *dev, unsigned int *p_class,
 			   unsigned int flags, u16 *id);
 extern int ata_dev_reread_id(struct ata_device *dev, unsigned int readid_flags);
-extern int ata_dev_revalidate(struct ata_device *dev, unsigned int readid_flags);
+extern int ata_dev_revalidate(struct ata_device *dev, unsigned int new_class,
+			      unsigned int readid_flags);
 extern int ata_dev_configure(struct ata_device *dev);
 extern int sata_down_spd_limit(struct ata_link *link);
 extern int sata_set_spd_needed(struct ata_link *link);
-- 
1.5.0.3



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

* [PATCH 04/12] libata-pmp-prep: make a number of functions global to libata
  2007-09-23  4:14 [PATCHSET 1/2] libata: prep for PMP support, take 6 Tejun Heo
                   ` (2 preceding siblings ...)
  2007-09-23  4:14 ` [PATCH 05/12] libata-pmp-prep: implement ops->qc_defer() Tejun Heo
@ 2007-09-23  4:14 ` Tejun Heo
  2007-09-23  4:14 ` [PATCH 07/12] libata-pmp-prep: implement ATA_LFLAG_NO_SRST, ASSUME_ATA and ASSUME_SEMB Tejun Heo
                   ` (7 subsequent siblings)
  11 siblings, 0 replies; 15+ messages in thread
From: Tejun Heo @ 2007-09-23  4:14 UTC (permalink / raw)
  To: jeff, alan, linux-ide; +Cc: Tejun Heo

Make a number of functions from libata-core.c and libata-eh.c global
to libata (drivers/ata/libata.h).  These will be used by PMP.

Signed-off-by: Tejun Heo <htejun@gmail.com>
---
 drivers/ata/libata-core.c |    4 ++--
 drivers/ata/libata-eh.c   |   31 +++++++++++++++----------------
 drivers/ata/libata.h      |   17 +++++++++++++++++
 3 files changed, 34 insertions(+), 18 deletions(-)

diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
index bcddc93..c33a1f8 100644
--- a/drivers/ata/libata-core.c
+++ b/drivers/ata/libata-core.c
@@ -6220,7 +6220,7 @@ void ata_dev_init(struct ata_device *dev)
  *	LOCKING:
  *	Kernel thread context (may sleep)
  */
-static void ata_link_init(struct ata_port *ap, struct ata_link *link, int pmp)
+void ata_link_init(struct ata_port *ap, struct ata_link *link, int pmp)
 {
 	int i;
 
@@ -6255,7 +6255,7 @@ static void ata_link_init(struct ata_port *ap, struct ata_link *link, int pmp)
  *	RETURNS:
  *	0 on success, -errno on failure.
  */
-static int sata_link_init_spd(struct ata_link *link)
+int sata_link_init_spd(struct ata_link *link)
 {
 	u32 scontrol, spd;
 	int rc;
diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c
index eb087bb..1d3b0dc 100644
--- a/drivers/ata/libata-eh.c
+++ b/drivers/ata/libata-eh.c
@@ -74,7 +74,6 @@ static const unsigned long ata_eh_reset_timeouts[] = {
 };
 
 static void __ata_port_freeze(struct ata_port *ap);
-static void ata_eh_finish(struct ata_port *ap);
 #ifdef CONFIG_PM
 static void ata_eh_handle_port_suspend(struct ata_port *ap);
 static void ata_eh_handle_port_resume(struct ata_port *ap);
@@ -1015,7 +1014,7 @@ void ata_eh_qc_retry(struct ata_queued_cmd *qc)
  *	LOCKING:
  *	None.
  */
-static void ata_eh_detach_dev(struct ata_device *dev)
+void ata_eh_detach_dev(struct ata_device *dev)
 {
 	struct ata_link *link = dev->link;
 	struct ata_port *ap = link->ap;
@@ -1052,8 +1051,8 @@ static void ata_eh_detach_dev(struct ata_device *dev)
  *	LOCKING:
  *	None.
  */
-static void ata_eh_about_to_do(struct ata_link *link, struct ata_device *dev,
-			       unsigned int action)
+void ata_eh_about_to_do(struct ata_link *link, struct ata_device *dev,
+			unsigned int action)
 {
 	struct ata_port *ap = link->ap;
 	struct ata_eh_info *ehi = &link->eh_info;
@@ -1095,8 +1094,8 @@ static void ata_eh_about_to_do(struct ata_link *link, struct ata_device *dev,
  *	LOCKING:
  *	None.
  */
-static void ata_eh_done(struct ata_link *link, struct ata_device *dev,
-			unsigned int action)
+void ata_eh_done(struct ata_link *link, struct ata_device *dev,
+		 unsigned int action)
 {
 	struct ata_eh_context *ehc = &link->eh_context;
 
@@ -1756,7 +1755,7 @@ static void ata_eh_link_autopsy(struct ata_link *link)
  *	LOCKING:
  *	Kernel thread context (may sleep).
  */
-static void ata_eh_autopsy(struct ata_port *ap)
+void ata_eh_autopsy(struct ata_port *ap)
 {
 	struct ata_link *link;
 
@@ -1867,7 +1866,7 @@ static void ata_eh_link_report(struct ata_link *link)
  *	LOCKING:
  *	None.
  */
-static void ata_eh_report(struct ata_port *ap)
+void ata_eh_report(struct ata_port *ap)
 {
 	struct ata_link *link;
 
@@ -1918,9 +1917,9 @@ static int ata_eh_followup_srst_needed(int rc, int classify,
 	return 0;
 }
 
-static int ata_eh_reset(struct ata_link *link, int classify,
-			ata_prereset_fn_t prereset, ata_reset_fn_t softreset,
-			ata_reset_fn_t hardreset, ata_postreset_fn_t postreset)
+int ata_eh_reset(struct ata_link *link, int classify,
+		 ata_prereset_fn_t prereset, ata_reset_fn_t softreset,
+		 ata_reset_fn_t hardreset, ata_postreset_fn_t postreset)
 {
 	struct ata_eh_context *ehc = &link->eh_context;
 	unsigned int *classes = ehc->classes;
@@ -2296,10 +2295,10 @@ static int ata_eh_handle_dev_fail(struct ata_device *dev, int err)
  *	RETURNS:
  *	0 on success, -errno on failure.
  */
-static int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset,
-			  ata_reset_fn_t softreset, ata_reset_fn_t hardreset,
-			  ata_postreset_fn_t postreset,
-			  struct ata_link **r_failed_link)
+int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset,
+		   ata_reset_fn_t softreset, ata_reset_fn_t hardreset,
+		   ata_postreset_fn_t postreset,
+		   struct ata_link **r_failed_link)
 {
 	struct ata_link *link;
 	struct ata_device *dev;
@@ -2445,7 +2444,7 @@ static int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset,
  *	LOCKING:
  *	None.
  */
-static void ata_eh_finish(struct ata_port *ap)
+void ata_eh_finish(struct ata_port *ap)
 {
 	int tag;
 
diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h
index 3457048..e380423 100644
--- a/drivers/ata/libata.h
+++ b/drivers/ata/libata.h
@@ -94,6 +94,8 @@ extern void ata_dev_select(struct ata_port *ap, unsigned int device,
 extern void swap_buf_le16(u16 *buf, unsigned int buf_words);
 extern int ata_flush_cache(struct ata_device *dev);
 extern void ata_dev_init(struct ata_device *dev);
+extern void ata_link_init(struct ata_port *ap, struct ata_link *link, int pmp);
+extern int sata_link_init_spd(struct ata_link *link);
 extern int ata_task_ioctl(struct scsi_device *scsidev, void __user *arg);
 extern int ata_cmd_ioctl(struct scsi_device *scsidev, void __user *arg);
 extern struct ata_port *ata_port_alloc(struct ata_host *host);
@@ -155,6 +157,21 @@ extern void ata_scsi_error(struct Scsi_Host *host);
 extern void ata_port_wait_eh(struct ata_port *ap);
 extern void ata_eh_fastdrain_timerfn(unsigned long arg);
 extern void ata_qc_schedule_eh(struct ata_queued_cmd *qc);
+extern void ata_eh_detach_dev(struct ata_device *dev);
+extern void ata_eh_about_to_do(struct ata_link *link, struct ata_device *dev,
+			       unsigned int action);
+extern void ata_eh_done(struct ata_link *link, struct ata_device *dev,
+			unsigned int action);
+extern void ata_eh_autopsy(struct ata_port *ap);
+extern void ata_eh_report(struct ata_port *ap);
+extern int ata_eh_reset(struct ata_link *link, int classify,
+			ata_prereset_fn_t prereset, ata_reset_fn_t softreset,
+			ata_reset_fn_t hardreset, ata_postreset_fn_t postreset);
+extern int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset,
+			  ata_reset_fn_t softreset, ata_reset_fn_t hardreset,
+			  ata_postreset_fn_t postreset,
+			  struct ata_link **r_failed_disk);
+extern void ata_eh_finish(struct ata_port *ap);
 
 /* libata-sff.c */
 extern u8 ata_irq_on(struct ata_port *ap);
-- 
1.5.0.3



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

* [PATCH 07/12] libata-pmp-prep: implement ATA_LFLAG_NO_SRST, ASSUME_ATA and ASSUME_SEMB
  2007-09-23  4:14 [PATCHSET 1/2] libata: prep for PMP support, take 6 Tejun Heo
                   ` (3 preceding siblings ...)
  2007-09-23  4:14 ` [PATCH 04/12] libata-pmp-prep: make a number of functions global to libata Tejun Heo
@ 2007-09-23  4:14 ` Tejun Heo
  2007-09-23  4:14 ` [PATCH 06/12] libata-pmp-prep: implement qc_defer helpers Tejun Heo
                   ` (6 subsequent siblings)
  11 siblings, 0 replies; 15+ messages in thread
From: Tejun Heo @ 2007-09-23  4:14 UTC (permalink / raw)
  To: jeff, alan, linux-ide; +Cc: Tejun Heo

Some links on some PMPs locks up on SRST and/or report incorrect
device signature.  Implement ATA_LFLAG_NO_SRST, ASSUME_ATA and
ASSUME_SEMB to handle these quirky links.  NO_SRST makes EH avoid
SRST.  ASSUME_ATA and SEMB forces class code to ATA and SEMB_UNSUP
respectively.  Note that SEMB isn't currently supported yet so the
_UNSUP variant is used.

Signed-off-by: Tejun Heo <htejun@gmail.com>
---
 drivers/ata/libata-eh.c |   42 ++++++++++++++++++++++++++++++++----------
 include/linux/libata.h  |    4 ++++
 2 files changed, 36 insertions(+), 10 deletions(-)

diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c
index 5244723..7be04bd 100644
--- a/drivers/ata/libata-eh.c
+++ b/drivers/ata/libata-eh.c
@@ -1906,14 +1906,18 @@ static int ata_do_reset(struct ata_link *link, ata_reset_fn_t reset,
 	return 0;
 }
 
-static int ata_eh_followup_srst_needed(int rc, int classify,
+static int ata_eh_followup_srst_needed(struct ata_link *link,
+				       int rc, int classify,
 				       const unsigned int *classes)
 {
+	if (link->flags & ATA_LFLAG_NO_SRST)
+		return 0;
 	if (rc == -EAGAIN)
 		return 1;
 	if (rc != 0)
 		return 0;
-	if (classify && classes[0] == ATA_DEV_UNKNOWN)
+	if (classify && !(link->flags & ATA_LFLAG_ASSUME_CLASS) &&
+	    classes[0] == ATA_DEV_UNKNOWN)
 		return 1;
 	return 0;
 }
@@ -1940,7 +1944,8 @@ int ata_eh_reset(struct ata_link *link, int classify,
 	 */
 	action = ehc->i.action;
 	ehc->i.action &= ~ATA_EH_RESET_MASK;
-	if (softreset && (!hardreset || (!sata_set_spd_needed(link) &&
+	if (softreset && (!hardreset || (!(link->flags & ATA_LFLAG_NO_SRST) &&
+					 !sata_set_spd_needed(link) &&
 					 !(action & ATA_EH_HARDRESET))))
 		ehc->i.action |= ATA_EH_SOFTRESET;
 	else
@@ -2003,7 +2008,7 @@ int ata_eh_reset(struct ata_link *link, int classify,
 	rc = ata_do_reset(link, reset, classes, deadline);
 
 	if (reset == hardreset &&
-	    ata_eh_followup_srst_needed(rc, classify, classes)) {
+	    ata_eh_followup_srst_needed(link, rc, classify, classes)) {
 		/* okay, let's do follow-up softreset */
 		reset = softreset;
 
@@ -2018,8 +2023,8 @@ int ata_eh_reset(struct ata_link *link, int classify,
 		ata_eh_about_to_do(link, NULL, ATA_EH_RESET_MASK);
 		rc = ata_do_reset(link, reset, classes, deadline);
 
-		if (rc == 0 && classify &&
-		    classes[0] == ATA_DEV_UNKNOWN) {
+		if (rc == 0 && classify && classes[0] == ATA_DEV_UNKNOWN &&
+		    !(link->flags & ATA_LFLAG_ASSUME_CLASS)) {
 			ata_link_printk(link, KERN_ERR,
 					"classification failed\n");
 			rc = -EINVAL;
@@ -2027,6 +2032,10 @@ int ata_eh_reset(struct ata_link *link, int classify,
 		}
 	}
 
+	/* if we skipped follow-up srst, clear rc */
+	if (rc == -EAGAIN)
+		rc = 0;
+
 	if (rc && try < ARRAY_SIZE(ata_eh_reset_timeouts)) {
 		unsigned long now = jiffies;
 
@@ -2051,12 +2060,25 @@ int ata_eh_reset(struct ata_link *link, int classify,
 	if (rc == 0) {
 		u32 sstatus;
 
-		/* After the reset, the device state is PIO 0 and the
-		 * controller state is undefined.  Record the mode.
-		 */
-		ata_link_for_each_dev(dev, link)
+		ata_link_for_each_dev(dev, link) {
+			/* After the reset, the device state is PIO 0
+			 * and the controller state is undefined.
+			 * Record the mode.
+			 */
 			dev->pio_mode = XFER_PIO_0;
 
+			if (ata_link_offline(link))
+				continue;
+
+			/* apply class override and convert UNKNOWN to NONE */
+			if (link->flags & ATA_LFLAG_ASSUME_ATA)
+				classes[dev->devno] = ATA_DEV_ATA;
+			else if (link->flags & ATA_LFLAG_ASSUME_SEMB)
+				classes[dev->devno] = ATA_DEV_SEMB_UNSUP; /* not yet */
+			else if (classes[dev->devno] == ATA_DEV_UNKNOWN)
+				classes[dev->devno] = ATA_DEV_NONE;
+		}
+
 		/* record current link speed */
 		if (sata_scr_read(link, SCR_STATUS, &sstatus) == 0)
 			link->sata_spd = (sstatus >> 4) & 0xf;
diff --git a/include/linux/libata.h b/include/linux/libata.h
index f9f81fd..6266fff 100644
--- a/include/linux/libata.h
+++ b/include/linux/libata.h
@@ -165,6 +165,10 @@ enum {
 	ATA_LFLAG_HRST_TO_RESUME = (1 << 0), /* hardreset to resume link */
 	ATA_LFLAG_SKIP_D2H_BSY	= (1 << 1), /* can't wait for the first D2H
 					     * Register FIS clearing BSY */
+	ATA_LFLAG_NO_SRST	= (1 << 2), /* avoid softreset */
+	ATA_LFLAG_ASSUME_ATA	= (1 << 3), /* assume ATA class */
+	ATA_LFLAG_ASSUME_SEMB	= (1 << 4), /* assume SEMB class */
+	ATA_LFLAG_ASSUME_CLASS	= ATA_LFLAG_ASSUME_ATA | ATA_LFLAG_ASSUME_SEMB,
 
 	/* struct ata_port flags */
 	ATA_FLAG_SLAVE_POSS	= (1 << 0), /* host supports slave dev */
-- 
1.5.0.3



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

* [PATCH 05/12] libata-pmp-prep: implement ops->qc_defer()
  2007-09-23  4:14 [PATCHSET 1/2] libata: prep for PMP support, take 6 Tejun Heo
  2007-09-23  4:14 ` [PATCH 01/12] libata: misc updates for AN Tejun Heo
  2007-09-23  4:14 ` [PATCH 08/12] libata-pmp-prep: implement ATA_LFLAG_NO_RETRY Tejun Heo
@ 2007-09-23  4:14 ` Tejun Heo
  2007-09-23  4:14 ` [PATCH 04/12] libata-pmp-prep: make a number of functions global to libata Tejun Heo
                   ` (8 subsequent siblings)
  11 siblings, 0 replies; 15+ messages in thread
From: Tejun Heo @ 2007-09-23  4:14 UTC (permalink / raw)
  To: jeff, alan, linux-ide; +Cc: Tejun Heo

Controllers which support PMP have various restrictions on which
combinations of commands are allowed to what number of devices
concurrently.  This patch implements ops->qc_defer() which determines
whether a qc can be issued at the moment or should be deferred.

If the function returns ATA_DEFER_LINK, the qc will be deferred until
a qc completes on the link.  If ATA_DEFER_PORT, until a qc completes
on any link.  The defer conditions are advisory and in general
ATA_DEFER_LINK can be considered as lower priority deferring than
ATA_DEFER_PORT.

ops->qc_defer() replaces fixed ata_scmd_need_defer().  For standard
NCQ/non-NCQ exclusion, ata_std_qc_defer() is implemented.  ahci and
sata_sil24 are converted to use ata_std_qc_defer().

ops->qc_defer() is heavier than the original mechanism because full qc
is prepped before determining to defer it, but various information is
needed to determine defer conditinos and fully translating a qc is the
only way to supply such information in generic manner.

IMHO, this shouldn't cause any noticeable performance issues as

* for most cases deferring occurs rarely (except for NCQ-aware
  cmd-switching PMP)
* translation itself isn't that expensive
* once deferred the command won't be repeated until another command
  completes which usually is a very long time cpu-wise.

Signed-off-by: Tejun Heo <htejun@gmail.com>
---
 drivers/ata/ahci.c        |    2 +
 drivers/ata/libata-core.c |   31 ++++++++++++++++++++++
 drivers/ata/libata-scsi.c |   62 +++++++++++++++++++--------------------------
 drivers/ata/sata_nv.c     |    1 +
 drivers/ata/sata_sil24.c  |    1 +
 include/linux/libata.h    |    6 ++++
 6 files changed, 67 insertions(+), 36 deletions(-)

diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c
index 0a6b694..cf34044 100644
--- a/drivers/ata/ahci.c
+++ b/drivers/ata/ahci.c
@@ -268,6 +268,7 @@ static const struct ata_port_operations ahci_ops = {
 
 	.tf_read		= ahci_tf_read,
 
+	.qc_defer		= ata_std_qc_defer,
 	.qc_prep		= ahci_qc_prep,
 	.qc_issue		= ahci_qc_issue,
 
@@ -298,6 +299,7 @@ static const struct ata_port_operations ahci_vt8251_ops = {
 
 	.tf_read		= ahci_tf_read,
 
+	.qc_defer		= ata_std_qc_defer,
 	.qc_prep		= ahci_qc_prep,
 	.qc_issue		= ahci_qc_issue,
 
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
index c33a1f8..7dd666e 100644
--- a/drivers/ata/libata-core.c
+++ b/drivers/ata/libata-core.c
@@ -4348,6 +4348,36 @@ int ata_check_atapi_dma(struct ata_queued_cmd *qc)
 }
 
 /**
+ *	ata_std_qc_defer - Check whether a qc needs to be deferred
+ *	@qc: ATA command in question
+ *
+ *	Non-NCQ commands cannot run with any other command, NCQ or
+ *	not.  As upper layer only knows the queue depth, we are
+ *	responsible for maintaining exclusion.  This function checks
+ *	whether a new command @qc can be issued.
+ *
+ *	LOCKING:
+ *	spin_lock_irqsave(host lock)
+ *
+ *	RETURNS:
+ *	ATA_DEFER_* if deferring is needed, 0 otherwise.
+ */
+int ata_std_qc_defer(struct ata_queued_cmd *qc)
+{
+	struct ata_link *link = qc->dev->link;
+
+	if (qc->tf.protocol == ATA_PROT_NCQ) {
+		if (!ata_tag_valid(link->active_tag))
+			return 0;
+	} else {
+		if (!ata_tag_valid(link->active_tag) && !link->sactive)
+			return 0;
+	}
+
+	return ATA_DEFER_LINK;
+}
+
+/**
  *	ata_qc_prep - Prepare taskfile for submission
  *	@qc: Metadata associated with taskfile to be prepared
  *
@@ -7113,6 +7143,7 @@ EXPORT_SYMBOL_GPL(ata_interrupt);
 EXPORT_SYMBOL_GPL(ata_do_set_mode);
 EXPORT_SYMBOL_GPL(ata_data_xfer);
 EXPORT_SYMBOL_GPL(ata_data_xfer_noirq);
+EXPORT_SYMBOL_GPL(ata_std_qc_defer);
 EXPORT_SYMBOL_GPL(ata_qc_prep);
 EXPORT_SYMBOL_GPL(ata_dumb_qc_prep);
 EXPORT_SYMBOL_GPL(ata_noop_qc_prep);
diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c
index 518d475..eb611b4 100644
--- a/drivers/ata/libata-scsi.c
+++ b/drivers/ata/libata-scsi.c
@@ -750,6 +750,13 @@ static void ata_scsi_sdev_config(struct scsi_device *sdev)
 {
 	sdev->use_10_for_rw = 1;
 	sdev->use_10_for_ms = 1;
+
+	/* Schedule policy is determined by ->qc_defer() callback and
+	 * it needs to see every deferred qc.  Set dev_blocked to 1 to
+	 * prevent SCSI midlayer from automatically deferring
+	 * requests.
+	 */
+	sdev->max_device_blocked = 1;
 }
 
 static void ata_scsi_dev_config(struct scsi_device *sdev,
@@ -1417,37 +1424,6 @@ static void ata_scsi_qc_complete(struct ata_queued_cmd *qc)
 }
 
 /**
- *	ata_scmd_need_defer - Check whether we need to defer scmd
- *	@dev: ATA device to which the command is addressed
- *	@is_io: Is the command IO (and thus possibly NCQ)?
- *
- *	NCQ and non-NCQ commands cannot run together.  As upper layer
- *	only knows the queue depth, we are responsible for maintaining
- *	exclusion.  This function checks whether a new command can be
- *	issued to @dev.
- *
- *	LOCKING:
- *	spin_lock_irqsave(host lock)
- *
- *	RETURNS:
- *	1 if deferring is needed, 0 otherwise.
- */
-static int ata_scmd_need_defer(struct ata_device *dev, int is_io)
-{
-	struct ata_link *link = dev->link;
-	int is_ncq = is_io && ata_ncq_enabled(dev);
-
-	if (is_ncq) {
-		if (!ata_tag_valid(link->active_tag))
-			return 0;
-	} else {
-		if (!ata_tag_valid(link->active_tag) && !link->sactive)
-			return 0;
-	}
-	return 1;
-}
-
-/**
  *	ata_scsi_translate - Translate then issue SCSI command to ATA device
  *	@dev: ATA device to which the command is addressed
  *	@cmd: SCSI command to execute
@@ -1478,14 +1454,12 @@ static int ata_scsi_translate(struct ata_device *dev, struct scsi_cmnd *cmd,
 			      void (*done)(struct scsi_cmnd *),
 			      ata_xlat_func_t xlat_func)
 {
+	struct ata_port *ap = dev->link->ap;
 	struct ata_queued_cmd *qc;
-	int is_io = xlat_func == ata_scsi_rw_xlat;
+	int rc;
 
 	VPRINTK("ENTER\n");
 
-	if (unlikely(ata_scmd_need_defer(dev, is_io)))
-		goto defer;
-
 	qc = ata_scsi_qc_new(dev, cmd, done);
 	if (!qc)
 		goto err_mem;
@@ -1509,6 +1483,11 @@ static int ata_scsi_translate(struct ata_device *dev, struct scsi_cmnd *cmd,
 	if (xlat_func(qc))
 		goto early_finish;
 
+	if (ap->ops->qc_defer) {
+		if ((rc = ap->ops->qc_defer(qc)))
+			goto defer;
+	}
+
 	/* select device, send command to hardware */
 	ata_qc_issue(qc);
 
@@ -1530,8 +1509,12 @@ err_mem:
 	return 0;
 
 defer:
+	ata_qc_free(qc);
 	DPRINTK("EXIT - defer\n");
-	return SCSI_MLQUEUE_DEVICE_BUSY;
+	if (rc == ATA_DEFER_LINK)
+		return SCSI_MLQUEUE_DEVICE_BUSY;
+	else
+		return SCSI_MLQUEUE_HOST_BUSY;
 }
 
 /**
@@ -2957,6 +2940,13 @@ int ata_scsi_add_hosts(struct ata_host *host, struct scsi_host_template *sht)
 		shost->max_channel = 1;
 		shost->max_cmd_len = 16;
 
+		/* Schedule policy is determined by ->qc_defer()
+		 * callback and it needs to see every deferred qc.
+		 * Set host_blocked to 1 to prevent SCSI midlayer from
+		 * automatically deferring requests.
+		 */
+		shost->max_host_blocked = 1;
+
 		rc = scsi_add_host(ap->scsi_host, ap->host->dev);
 		if (rc)
 			goto err_add;
diff --git a/drivers/ata/sata_nv.c b/drivers/ata/sata_nv.c
index b860f99..40557fe 100644
--- a/drivers/ata/sata_nv.c
+++ b/drivers/ata/sata_nv.c
@@ -423,6 +423,7 @@ static const struct ata_port_operations nv_adma_ops = {
 	.bmdma_start		= ata_bmdma_start,
 	.bmdma_stop		= ata_bmdma_stop,
 	.bmdma_status		= ata_bmdma_status,
+	.qc_defer		= ata_std_qc_defer,
 	.qc_prep		= nv_adma_qc_prep,
 	.qc_issue		= nv_adma_qc_issue,
 	.freeze			= nv_adma_freeze,
diff --git a/drivers/ata/sata_sil24.c b/drivers/ata/sata_sil24.c
index d9c010a..9acfce4 100644
--- a/drivers/ata/sata_sil24.c
+++ b/drivers/ata/sata_sil24.c
@@ -393,6 +393,7 @@ static const struct ata_port_operations sil24_ops = {
 
 	.tf_read		= sil24_tf_read,
 
+	.qc_defer		= ata_std_qc_defer,
 	.qc_prep		= sil24_qc_prep,
 	.qc_issue		= sil24_qc_issue,
 
diff --git a/include/linux/libata.h b/include/linux/libata.h
index c3820f1..b0d4ca0 100644
--- a/include/linux/libata.h
+++ b/include/linux/libata.h
@@ -272,6 +272,10 @@ enum {
 	/* ering size */
 	ATA_ERING_SIZE		= 32,
 
+	/* return values for ->qc_defer */
+	ATA_DEFER_LINK		= 1,
+	ATA_DEFER_PORT		= 2,
+
 	/* desc_len for ata_eh_info and context */
 	ATA_EH_DESC_LEN		= 80,
 
@@ -639,6 +643,7 @@ struct ata_port_operations {
 
 	void (*data_xfer) (struct ata_device *, unsigned char *, unsigned int, int);
 
+	int (*qc_defer) (struct ata_queued_cmd *qc);
 	void (*qc_prep) (struct ata_queued_cmd *qc);
 	unsigned int (*qc_issue) (struct ata_queued_cmd *qc);
 
@@ -824,6 +829,7 @@ extern void ata_data_xfer(struct ata_device *adev, unsigned char *buf,
 			  unsigned int buflen, int write_data);
 extern void ata_data_xfer_noirq(struct ata_device *adev, unsigned char *buf,
 				unsigned int buflen, int write_data);
+extern int ata_std_qc_defer(struct ata_queued_cmd *qc);
 extern void ata_dumb_qc_prep(struct ata_queued_cmd *qc);
 extern void ata_qc_prep(struct ata_queued_cmd *qc);
 extern void ata_noop_qc_prep(struct ata_queued_cmd *qc);
-- 
1.5.0.3



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

* [PATCH 08/12] libata-pmp-prep: implement ATA_LFLAG_NO_RETRY
  2007-09-23  4:14 [PATCHSET 1/2] libata: prep for PMP support, take 6 Tejun Heo
  2007-09-23  4:14 ` [PATCH 01/12] libata: misc updates for AN Tejun Heo
@ 2007-09-23  4:14 ` Tejun Heo
  2007-09-23  4:14 ` [PATCH 05/12] libata-pmp-prep: implement ops->qc_defer() Tejun Heo
                   ` (9 subsequent siblings)
  11 siblings, 0 replies; 15+ messages in thread
From: Tejun Heo @ 2007-09-23  4:14 UTC (permalink / raw)
  To: jeff, alan, linux-ide; +Cc: Tejun Heo

Some PMP links are connected to internal pseudo devices which may come
and go depending on situation.  There's no reason to try hard to
recover them.  ATA_LFLAG_NO_RETRY tells EH to not retry if the device
attached to the link fails.

Signed-off-by: Tejun Heo <htejun@gmail.com>
---
 drivers/ata/libata-eh.c |    5 ++++-
 include/linux/libata.h  |    1 +
 2 files changed, 5 insertions(+), 1 deletions(-)

diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c
index 7be04bd..8f8ed4d 100644
--- a/drivers/ata/libata-eh.c
+++ b/drivers/ata/libata-eh.c
@@ -2335,7 +2335,10 @@ int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset,
 		struct ata_eh_context *ehc = &link->eh_context;
 
 		ata_link_for_each_dev(dev, link) {
-			ehc->tries[dev->devno] = ATA_EH_DEV_TRIES;
+			if (link->flags & ATA_LFLAG_NO_RETRY)
+				ehc->tries[dev->devno] = 1;
+			else
+				ehc->tries[dev->devno] = ATA_EH_DEV_TRIES;
 
 			/* collect port action mask recorded in dev actions */
 			ehc->i.action |= ehc->i.dev_action[dev->devno] &
diff --git a/include/linux/libata.h b/include/linux/libata.h
index 6266fff..adeee73 100644
--- a/include/linux/libata.h
+++ b/include/linux/libata.h
@@ -169,6 +169,7 @@ enum {
 	ATA_LFLAG_ASSUME_ATA	= (1 << 3), /* assume ATA class */
 	ATA_LFLAG_ASSUME_SEMB	= (1 << 4), /* assume SEMB class */
 	ATA_LFLAG_ASSUME_CLASS	= ATA_LFLAG_ASSUME_ATA | ATA_LFLAG_ASSUME_SEMB,
+	ATA_LFLAG_NO_RETRY	= (1 << 5), /* don't retry this link */
 
 	/* struct ata_port flags */
 	ATA_FLAG_SLAVE_POSS	= (1 << 0), /* host supports slave dev */
-- 
1.5.0.3



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

* [PATCH 06/12] libata-pmp-prep: implement qc_defer helpers
  2007-09-23  4:14 [PATCHSET 1/2] libata: prep for PMP support, take 6 Tejun Heo
                   ` (4 preceding siblings ...)
  2007-09-23  4:14 ` [PATCH 07/12] libata-pmp-prep: implement ATA_LFLAG_NO_SRST, ASSUME_ATA and ASSUME_SEMB Tejun Heo
@ 2007-09-23  4:14 ` Tejun Heo
  2007-09-23  4:14 ` [PATCH 03/12] libata-pmp-prep: add @new_class to ata_dev_revalidate() Tejun Heo
                   ` (5 subsequent siblings)
  11 siblings, 0 replies; 15+ messages in thread
From: Tejun Heo @ 2007-09-23  4:14 UTC (permalink / raw)
  To: jeff, alan, linux-ide; +Cc: Tejun Heo

Implement ap->nr_active_links (the number of links with active qcs),
ap->excl_link (pointer to link which can be used by ->qc_defer and is
cleared when a qc with ATA_QCFLAG_CLEAR_EXCL completes), and
ata_link_active().

These can be used by ->qc_defer() to implement proper command
exclusion.  This set of helpers seem enough for both sil24 (ATAPI
exclusion needed) and cmd-switching PMP.

Signed-off-by: Tejun Heo <htejun@gmail.com>
---
 drivers/ata/libata-core.c |   22 ++++++++++++++++++++--
 drivers/ata/libata-eh.c   |    5 +++++
 include/linux/libata.h    |    8 ++++++++
 3 files changed, 33 insertions(+), 2 deletions(-)

diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
index 7dd666e..86ef06e 100644
--- a/drivers/ata/libata-core.c
+++ b/drivers/ata/libata-core.c
@@ -1392,6 +1392,7 @@ unsigned ata_exec_internal_sg(struct ata_device *dev,
 	struct ata_queued_cmd *qc;
 	unsigned int tag, preempted_tag;
 	u32 preempted_sactive, preempted_qc_active;
+	int preempted_nr_active_links;
 	DECLARE_COMPLETION_ONSTACK(wait);
 	unsigned long flags;
 	unsigned int err_mask;
@@ -1430,9 +1431,11 @@ unsigned ata_exec_internal_sg(struct ata_device *dev,
 	preempted_tag = link->active_tag;
 	preempted_sactive = link->sactive;
 	preempted_qc_active = ap->qc_active;
+	preempted_nr_active_links = ap->nr_active_links;
 	link->active_tag = ATA_TAG_POISON;
 	link->sactive = 0;
 	ap->qc_active = 0;
+	ap->nr_active_links = 0;
 
 	/* prepare & issue qc */
 	qc->tf = *tf;
@@ -1511,6 +1514,7 @@ unsigned ata_exec_internal_sg(struct ata_device *dev,
 	link->active_tag = preempted_tag;
 	link->sactive = preempted_sactive;
 	ap->qc_active = preempted_qc_active;
+	ap->nr_active_links = preempted_nr_active_links;
 
 	/* XXX - Some LLDDs (sata_mv) disable port on command failure.
 	 * Until those drivers are fixed, we detect the condition
@@ -5410,10 +5414,19 @@ void __ata_qc_complete(struct ata_queued_cmd *qc)
 		ata_sg_clean(qc);
 
 	/* command should be marked inactive atomically with qc completion */
-	if (qc->tf.protocol == ATA_PROT_NCQ)
+	if (qc->tf.protocol == ATA_PROT_NCQ) {
 		link->sactive &= ~(1 << qc->tag);
-	else
+		if (!link->sactive)
+			ap->nr_active_links--;
+	} else {
 		link->active_tag = ATA_TAG_POISON;
+		ap->nr_active_links--;
+	}
+
+	/* clear exclusive status */
+	if (unlikely(qc->flags & ATA_QCFLAG_CLEAR_EXCL &&
+		     ap->excl_link == link))
+		ap->excl_link = NULL;
 
 	/* atapi: mark qc as inactive to prevent the interrupt handler
 	 * from completing the command twice later, before the error handler
@@ -5592,9 +5605,14 @@ void ata_qc_issue(struct ata_queued_cmd *qc)
 
 	if (qc->tf.protocol == ATA_PROT_NCQ) {
 		WARN_ON(link->sactive & (1 << qc->tag));
+
+		if (!link->sactive)
+			ap->nr_active_links++;
 		link->sactive |= 1 << qc->tag;
 	} else {
 		WARN_ON(link->sactive);
+
+		ap->nr_active_links++;
 		link->active_tag = qc->tag;
 	}
 
diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c
index 1d3b0dc..5244723 100644
--- a/drivers/ata/libata-eh.c
+++ b/drivers/ata/libata-eh.c
@@ -451,6 +451,7 @@ void ata_scsi_error(struct Scsi_Host *host)
 
 		ap->pflags |= ATA_PFLAG_EH_IN_PROGRESS;
 		ap->pflags &= ~ATA_PFLAG_EH_PENDING;
+		ap->excl_link = NULL;	/* don't maintain exclusion over EH */
 
 		spin_unlock_irqrestore(ap->lock, flags);
 
@@ -2474,6 +2475,10 @@ void ata_eh_finish(struct ata_port *ap)
 			}
 		}
 	}
+
+	/* make sure nr_active_links is zero after EH */
+	WARN_ON(ap->nr_active_links);
+	ap->nr_active_links = 0;
 }
 
 /**
diff --git a/include/linux/libata.h b/include/linux/libata.h
index b0d4ca0..f9f81fd 100644
--- a/include/linux/libata.h
+++ b/include/linux/libata.h
@@ -216,6 +216,7 @@ enum {
 	ATA_QCFLAG_DMAMAP	= ATA_QCFLAG_SG | ATA_QCFLAG_SINGLE,
 	ATA_QCFLAG_IO		= (1 << 3), /* standard IO command */
 	ATA_QCFLAG_RESULT_TF	= (1 << 4), /* result TF requested */
+	ATA_QCFLAG_CLEAR_EXCL	= (1 << 5), /* clear excl_link on completion */
 
 	ATA_QCFLAG_FAILED	= (1 << 16), /* cmd failed and is owned by EH */
 	ATA_QCFLAG_SENSE_VALID	= (1 << 17), /* sense data valid */
@@ -579,11 +580,13 @@ struct ata_port {
 	struct ata_queued_cmd	qcmd[ATA_MAX_QUEUE];
 	unsigned long		qc_allocated;
 	unsigned int		qc_active;
+	int			nr_active_links; /* #links with active qcs */
 
 	struct ata_link		link;	/* host default link */
 
 	int			nr_pmp_links;	/* nr of available PMP links */
 	struct ata_link		*pmp_link;	/* array of PMP links */
+	struct ata_link		*excl_link;	/* for PMP qc exclusion */
 
 	struct ata_port_stats	stats;
 	struct ata_host		*host;
@@ -1104,6 +1107,11 @@ static inline int ata_link_max_devices(const struct ata_link *link)
 	return 1;
 }
 
+static inline int ata_link_active(struct ata_link *link)
+{
+	return ata_tag_valid(link->active_tag) || link->sactive;
+}
+
 static inline struct ata_link *ata_port_first_link(struct ata_port *ap)
 {
 	if (ap->nr_pmp_links)
-- 
1.5.0.3



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

* [PATCH 11/12] libata-pmp-prep: implement ATA_HORKAGE_SKIP_PM
  2007-09-23  4:14 [PATCHSET 1/2] libata: prep for PMP support, take 6 Tejun Heo
                   ` (10 preceding siblings ...)
  2007-09-23  4:14 ` [PATCH 09/12] libata-pmp-prep: implement ATA_LFLAG_DISABLED Tejun Heo
@ 2007-09-23  4:14 ` Tejun Heo
  11 siblings, 0 replies; 15+ messages in thread
From: Tejun Heo @ 2007-09-23  4:14 UTC (permalink / raw)
  To: jeff, alan, linux-ide; +Cc: Tejun Heo

Some pseudo devices fail PM commands unnecessarily aborting system
suspend.  Implement ATA_HORKAGE_SKIP_PM which makes libata skip PM
commands for these devices.

Signed-off-by: Tejun Heo <htejun@gmail.com>
---
 drivers/ata/libata-scsi.c |    7 +++++++
 include/linux/libata.h    |    1 +
 2 files changed, 8 insertions(+), 0 deletions(-)

diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c
index eb611b4..a651bdd 100644
--- a/drivers/ata/libata-scsi.c
+++ b/drivers/ata/libata-scsi.c
@@ -945,6 +945,13 @@ static unsigned int ata_scsi_start_stop_xlat(struct ata_queued_cmd *qc)
 		goto invalid_fld;       /* LOEJ bit set not supported */
 	if (((cdb[4] >> 4) & 0xf) != 0)
 		goto invalid_fld;       /* power conditions not supported */
+
+	if (qc->dev->horkage & ATA_HORKAGE_SKIP_PM) {
+		/* the device lacks PM support, finish without doing anything */
+		scmd->result = SAM_STAT_GOOD;
+		return 1;
+	}
+
 	if (cdb[4] & 0x1) {
 		tf->nsect = 1;	/* 1 sector, lba=0 */
 
diff --git a/include/linux/libata.h b/include/linux/libata.h
index 2bd1d26..56b2187 100644
--- a/include/linux/libata.h
+++ b/include/linux/libata.h
@@ -329,6 +329,7 @@ enum {
 	ATA_HORKAGE_NONCQ	= (1 << 2),	/* Don't use NCQ */
 	ATA_HORKAGE_MAX_SEC_128	= (1 << 3),	/* Limit max sects to 128 */
 	ATA_HORKAGE_BROKEN_HPA	= (1 << 4),	/* Broken HPA */
+	ATA_HORKAGE_SKIP_PM	= (1 << 5),	/* Skip PM operations */
 };
 
 enum hsm_task_states {
-- 
1.5.0.3



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

* [PATCH 09/12] libata-pmp-prep: implement ATA_LFLAG_DISABLED
  2007-09-23  4:14 [PATCHSET 1/2] libata: prep for PMP support, take 6 Tejun Heo
                   ` (9 preceding siblings ...)
  2007-09-23  4:14 ` [PATCH 10/12] libata-pmp-prep: implement EH fast-fail path Tejun Heo
@ 2007-09-23  4:14 ` Tejun Heo
  2007-09-23  4:14 ` [PATCH 11/12] libata-pmp-prep: implement ATA_HORKAGE_SKIP_PM Tejun Heo
  11 siblings, 0 replies; 15+ messages in thread
From: Tejun Heo @ 2007-09-23  4:14 UTC (permalink / raw)
  To: jeff, alan, linux-ide; +Cc: Tejun Heo

Implement ATA_LFLAG_DISABLED.  The flag indicates the link is disabled
due to EH recovery failure.  While a link is disabled, no EH action is
taken on the link and suspend/resume become noop too.

This will be used by PMP links to manage failed links.

Signed-off-by: Tejun Heo <htejun@gmail.com>
---
 drivers/ata/libata-eh.c |   30 +++++++++++++++++++++++++++++-
 include/linux/libata.h  |    3 +++
 2 files changed, 32 insertions(+), 1 deletions(-)

diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c
index 8f8ed4d..fbbf791 100644
--- a/drivers/ata/libata-eh.c
+++ b/drivers/ata/libata-eh.c
@@ -1308,6 +1308,7 @@ static void ata_eh_analyze_serror(struct ata_link *link)
 	struct ata_eh_context *ehc = &link->eh_context;
 	u32 serror = ehc->i.serror;
 	unsigned int err_mask = 0, action = 0;
+	u32 hotplug_mask;
 
 	if (serror & SERR_PERSISTENT) {
 		err_mask |= AC_ERR_ATA_BUS;
@@ -1326,7 +1327,20 @@ static void ata_eh_analyze_serror(struct ata_link *link)
 		err_mask |= AC_ERR_SYSTEM;
 		action |= ATA_EH_HARDRESET;
 	}
-	if (serror & (SERR_PHYRDY_CHG | SERR_DEV_XCHG))
+
+	/* Determine whether a hotplug event has occurred.  Both
+	 * SError.N/X are considered hotplug events for enabled or
+	 * host links.  For disabled PMP links, only N bit is
+	 * considered as X bit is left at 1 for link plugging.
+	 */
+	hotplug_mask = 0;
+
+	if (!(link->flags & ATA_LFLAG_DISABLED) || ata_is_host_link(link))
+		hotplug_mask = SERR_PHYRDY_CHG | SERR_DEV_XCHG;
+	else
+		hotplug_mask = SERR_PHYRDY_CHG;
+
+	if (serror & hotplug_mask)
 		ata_ehi_hotplugged(&ehc->i);
 
 	ehc->i.err_mask |= err_mask;
@@ -2227,6 +2241,10 @@ static int ata_eh_skip_recovery(struct ata_link *link)
 	struct ata_eh_context *ehc = &link->eh_context;
 	struct ata_device *dev;
 
+	/* skip disabled links */
+	if (link->flags & ATA_LFLAG_DISABLED)
+		return 1;
+
 	/* thaw frozen port, resume link and recover failed devices */
 	if ((link->ap->pflags & ATA_PFLAG_FROZEN) ||
 	    (ehc->i.flags & ATA_EHI_RESUME_LINK) || ata_link_nr_enabled(link))
@@ -2327,6 +2345,7 @@ int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset,
 	struct ata_device *dev;
 	int nr_failed_devs, nr_disabled_devs;
 	int reset, rc;
+	unsigned long flags;
 
 	DPRINTK("ENTER\n");
 
@@ -2334,6 +2353,15 @@ int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset,
 	ata_port_for_each_link(link, ap) {
 		struct ata_eh_context *ehc = &link->eh_context;
 
+		/* re-enable link? */
+		if (ehc->i.action & ATA_EH_ENABLE_LINK) {
+			ata_eh_about_to_do(link, NULL, ATA_EH_ENABLE_LINK);
+			spin_lock_irqsave(ap->lock, flags);
+			link->flags &= ~ATA_LFLAG_DISABLED;
+			spin_unlock_irqrestore(ap->lock, flags);
+			ata_eh_done(link, NULL, ATA_EH_ENABLE_LINK);
+		}
+
 		ata_link_for_each_dev(dev, link) {
 			if (link->flags & ATA_LFLAG_NO_RETRY)
 				ehc->tries[dev->devno] = 1;
diff --git a/include/linux/libata.h b/include/linux/libata.h
index adeee73..2bd1d26 100644
--- a/include/linux/libata.h
+++ b/include/linux/libata.h
@@ -170,6 +170,7 @@ enum {
 	ATA_LFLAG_ASSUME_SEMB	= (1 << 4), /* assume SEMB class */
 	ATA_LFLAG_ASSUME_CLASS	= ATA_LFLAG_ASSUME_ATA | ATA_LFLAG_ASSUME_SEMB,
 	ATA_LFLAG_NO_RETRY	= (1 << 5), /* don't retry this link */
+	ATA_LFLAG_DISABLED	= (1 << 6), /* link is disabled */
 
 	/* struct ata_port flags */
 	ATA_FLAG_SLAVE_POSS	= (1 << 0), /* host supports slave dev */
@@ -289,6 +290,7 @@ enum {
 	ATA_EH_REVALIDATE	= (1 << 0),
 	ATA_EH_SOFTRESET	= (1 << 1),
 	ATA_EH_HARDRESET	= (1 << 2),
+	ATA_EH_ENABLE_LINK	= (1 << 3),
 
 	ATA_EH_RESET_MASK	= ATA_EH_SOFTRESET | ATA_EH_HARDRESET,
 	ATA_EH_PERDEV_MASK	= ATA_EH_REVALIDATE,
@@ -999,6 +1001,7 @@ static inline void ata_ehi_hotplugged(struct ata_eh_info *ehi)
 {
 	ata_ehi_schedule_probe(ehi);
 	ehi->flags |= ATA_EHI_HOTPLUGGED;
+	ehi->action |= ATA_EH_ENABLE_LINK;
 	ehi->err_mask |= AC_ERR_ATA_BUS;
 }
 
-- 
1.5.0.3



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

* [PATCH 12/12] libata-pmp-prep: implement sata_async_notification()
  2007-09-23  4:14 [PATCHSET 1/2] libata: prep for PMP support, take 6 Tejun Heo
                   ` (7 preceding siblings ...)
  2007-09-23  4:14 ` [PATCH 02/12] libata-pmp-prep: add PMP related constants, fields, ops and update helpers Tejun Heo
@ 2007-09-23  4:14 ` Tejun Heo
  2007-09-23  4:14 ` [PATCH 10/12] libata-pmp-prep: implement EH fast-fail path Tejun Heo
                   ` (2 subsequent siblings)
  11 siblings, 0 replies; 15+ messages in thread
From: Tejun Heo @ 2007-09-23  4:14 UTC (permalink / raw)
  To: jeff, alan, linux-ide; +Cc: Tejun Heo

AN serves multiple purposes.  For ATAPI, it's used for media change
notification.  For PMP, for downstream PHY status change notification.
Implement sata_async_notification() which demultiplexes AN.

To avoid unnecessary port events, ATAPI AN is not enabled if PMP is
attached but SNTF is not available.

Signed-off-by: Tejun Heo <htejun@gmail.com>
Cc: Kriten Carlson Accardi <kristen.c.accardi@intel.com>
---
 drivers/ata/ahci.c        |   24 ++++----------
 drivers/ata/libata-core.c |   13 +++++--
 drivers/ata/libata-eh.c   |   73 +++++++++++++++++++++++++++++++++++++++++++++
 drivers/ata/libata-scsi.c |    1 -
 drivers/ata/libata.h      |    1 +
 drivers/ata/sata_sil24.c  |    5 +--
 include/linux/libata.h    |    4 +-
 7 files changed, 93 insertions(+), 28 deletions(-)

diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c
index cf34044..9f3c591 100644
--- a/drivers/ata/ahci.c
+++ b/drivers/ata/ahci.c
@@ -1356,27 +1356,17 @@ static void ahci_port_intr(struct ata_port *ap)
 	}
 
 	if (status & PORT_IRQ_SDB_FIS) {
-		/*
-		 * if this is an ATAPI device with AN turned on,
-		 * then we should interrogate the device to
-		 * determine the cause of the interrupt
-		 *
-		 * for AN - this we should check the SDB FIS
-		 * and find the I and N bits set
+		/* If the 'N' bit in word 0 of the FIS is set, we just
+		 * received asynchronous notification.  Tell libata
+		 * about it.  Note that as the SDB FIS itself is
+		 * accessible, SNotification can be emulated by the
+		 * driver but don't bother for the time being.
 		 */
 		const __le32 *f = pp->rx_fis + RX_FIS_SDB;
 		u32 f0 = le32_to_cpu(f[0]);
 
-		/* check the 'N' bit in word 0 of the FIS */
-		if (f0 & (1 << 15)) {
-			int port_addr = ((f0 & 0x00000f00) >> 8);
-			struct ata_device *adev;
-			if (port_addr < ATA_MAX_DEVICES) {
-				adev = &ap->link.device[port_addr];
-				if (adev->flags & ATA_DFLAG_AN)
-					ata_scsi_media_change_notify(adev);
-			}
-		}
+		if (f0 & (1 << 15))
+			sata_async_notification(ap);
 	}
 
 	if (ap->link.sactive)
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
index 86ef06e..e650fb9 100644
--- a/drivers/ata/libata-core.c
+++ b/drivers/ata/libata-core.c
@@ -2018,6 +2018,7 @@ int ata_dev_configure(struct ata_device *dev)
 	else if (dev->class == ATA_DEV_ATAPI) {
 		const char *cdb_intr_string = "";
 		const char *atapi_an_string = "";
+		u32 sntf;
 
 		rc = atapi_cdb_len(id);
 		if ((rc < 12) || (rc > ATAPI_CDB_LEN)) {
@@ -2029,11 +2030,14 @@ int ata_dev_configure(struct ata_device *dev)
 		}
 		dev->cdb_len = (unsigned int) rc;
 
-		/*
-		 * check to see if this ATAPI device supports
-		 * Asynchronous Notification
+		/* Enable ATAPI AN if both the host and device have
+		 * the support.  If PMP is attached, SNTF is required
+		 * to enable ATAPI AN to discern between PHY status
+		 * changed notifications and ATAPI ANs.
 		 */
-		if ((ap->flags & ATA_FLAG_AN) && ata_id_has_atapi_AN(id)) {
+		if ((ap->flags & ATA_FLAG_AN) && ata_id_has_atapi_AN(id) &&
+		    (!ap->nr_pmp_links ||
+		     sata_scr_read(&ap->link, SCR_NOTIFICATION, &sntf) == 0)) {
 			unsigned int err_mask;
 
 			/* issue SET feature command to turn this on */
@@ -7250,6 +7254,7 @@ EXPORT_SYMBOL_GPL(ata_port_schedule_eh);
 EXPORT_SYMBOL_GPL(ata_link_abort);
 EXPORT_SYMBOL_GPL(ata_port_abort);
 EXPORT_SYMBOL_GPL(ata_port_freeze);
+EXPORT_SYMBOL_GPL(sata_async_notification);
 EXPORT_SYMBOL_GPL(ata_eh_freeze_port);
 EXPORT_SYMBOL_GPL(ata_eh_thaw_port);
 EXPORT_SYMBOL_GPL(ata_eh_qc_complete);
diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c
index 3c31e10..60186f8 100644
--- a/drivers/ata/libata-eh.c
+++ b/drivers/ata/libata-eh.c
@@ -905,6 +905,79 @@ int ata_port_freeze(struct ata_port *ap)
 }
 
 /**
+ *	sata_async_notification - SATA async notification handler
+ *	@ap: ATA port where async notification is received
+ *
+ *	Handler to be called when async notification via SDB FIS is
+ *	received.  This function schedules EH if necessary.
+ *
+ *	LOCKING:
+ *	spin_lock_irqsave(host lock)
+ *
+ *	RETURNS:
+ *	1 if EH is scheduled, 0 otherwise.
+ */
+int sata_async_notification(struct ata_port *ap)
+{
+	u32 sntf;
+	int rc;
+
+	if (!(ap->flags & ATA_FLAG_AN))
+		return 0;
+
+	rc = sata_scr_read(&ap->link, SCR_NOTIFICATION, &sntf);
+	if (rc == 0)
+		sata_scr_write(&ap->link, SCR_NOTIFICATION, sntf);
+
+	if (!ap->nr_pmp_links || rc) {
+		/* PMP is not attached or SNTF is not available */
+		if (!ap->nr_pmp_links) {
+			/* PMP is not attached.  Check whether ATAPI
+			 * AN is configured.  If so, notify media
+			 * change.
+			 */
+			struct ata_device *dev = ap->link.device;
+
+			if ((dev->class == ATA_DEV_ATAPI) &&
+			    (dev->flags & ATA_DFLAG_AN))
+				ata_scsi_media_change_notify(dev);
+			return 0;
+		} else {
+			/* PMP is attached but SNTF is not available.
+			 * ATAPI async media change notification is
+			 * not used.  The PMP must be reporting PHY
+			 * status change, schedule EH.
+			 */
+			ata_port_schedule_eh(ap);
+			return 1;
+		}
+	} else {
+		/* PMP is attached and SNTF is available */
+		struct ata_link *link;
+
+		/* check and notify ATAPI AN */
+		ata_port_for_each_link(link, ap) {
+			if (!(sntf & (1 << link->pmp)))
+				continue;
+
+			if ((link->device->class == ATA_DEV_ATAPI) &&
+			    (link->device->flags & ATA_DFLAG_AN))
+				ata_scsi_media_change_notify(link->device);
+		}
+
+		/* If PMP is reporting that PHY status of some
+		 * downstream ports has changed, schedule EH.
+		 */
+		if (sntf & (1 << SATA_PMP_CTRL_PORT)) {
+			ata_port_schedule_eh(ap);
+			return 1;
+		}
+
+		return 0;
+	}
+}
+
+/**
  *	ata_eh_freeze_port - EH helper to freeze port
  *	@ap: ATA port to freeze
  *
diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c
index a651bdd..116d875 100644
--- a/drivers/ata/libata-scsi.c
+++ b/drivers/ata/libata-scsi.c
@@ -3167,7 +3167,6 @@ void ata_scsi_media_change_notify(struct ata_device *dev)
 		scsi_device_event_notify(dev->sdev, SDEV_MEDIA_CHANGE);
 #endif
 }
-EXPORT_SYMBOL_GPL(ata_scsi_media_change_notify);
 
 /**
  *	ata_scsi_hotplug - SCSI part of hotplug
diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h
index e380423..fc8a786 100644
--- a/drivers/ata/libata.h
+++ b/drivers/ata/libata.h
@@ -118,6 +118,7 @@ extern int ata_scsi_add_hosts(struct ata_host *host,
 			      struct scsi_host_template *sht);
 extern void ata_scsi_scan_host(struct ata_port *ap, int sync);
 extern int ata_scsi_offline_dev(struct ata_device *dev);
+extern void ata_scsi_media_change_notify(struct ata_device *dev);
 extern void ata_scsi_hotplug(struct work_struct *work);
 extern unsigned int ata_scsiop_inq_std(struct ata_scsi_args *args, u8 *rbuf,
 			       unsigned int buflen);
diff --git a/drivers/ata/sata_sil24.c b/drivers/ata/sata_sil24.c
index 9acfce4..b4f81eb 100644
--- a/drivers/ata/sata_sil24.c
+++ b/drivers/ata/sata_sil24.c
@@ -821,11 +821,8 @@ static void sil24_error_intr(struct ata_port *ap)
 	ata_ehi_push_desc(ehi, "irq_stat 0x%08x", irq_stat);
 
 	if (irq_stat & PORT_IRQ_SDB_NOTIFY) {
-		struct ata_device *dev = ap->link.device;
-
 		ata_ehi_push_desc(ehi, "SDB notify");
-		if (dev->flags & ATA_DFLAG_AN)
-			ata_scsi_media_change_notify(dev);
+		sata_async_notification(ap);
 	}
 
 	if (irq_stat & (PORT_IRQ_PHYRDY_CHG | PORT_IRQ_DEV_XCHG)) {
diff --git a/include/linux/libata.h b/include/linux/libata.h
index 56b2187..cd9c2a2 100644
--- a/include/linux/libata.h
+++ b/include/linux/libata.h
@@ -139,7 +139,7 @@ enum {
 	ATA_DFLAG_FLUSH_EXT	= (1 << 4), /* do FLUSH_EXT instead of FLUSH */
 	ATA_DFLAG_ACPI_PENDING	= (1 << 5), /* ACPI resume action pending */
 	ATA_DFLAG_ACPI_FAILED	= (1 << 6), /* ACPI on devcfg has failed */
-	ATA_DFLAG_AN		= (1 << 7), /* device supports AN */
+	ATA_DFLAG_AN		= (1 << 7), /* AN configured */
 	ATA_DFLAG_CFG_MASK	= (1 << 12) - 1,
 
 	ATA_DFLAG_PIO		= (1 << 12), /* device limited to PIO mode */
@@ -787,7 +787,6 @@ extern void ata_host_init(struct ata_host *, struct device *,
 extern int ata_scsi_detect(struct scsi_host_template *sht);
 extern int ata_scsi_ioctl(struct scsi_device *dev, int cmd, void __user *arg);
 extern int ata_scsi_queuecmd(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *));
-extern void ata_scsi_media_change_notify(struct ata_device *atadev);
 extern void ata_sas_port_destroy(struct ata_port *);
 extern struct ata_port *ata_sas_port_alloc(struct ata_host *,
 					   struct ata_port_info *, struct Scsi_Host *);
@@ -953,6 +952,7 @@ extern void ata_port_schedule_eh(struct ata_port *ap);
 extern int ata_link_abort(struct ata_link *link);
 extern int ata_port_abort(struct ata_port *ap);
 extern int ata_port_freeze(struct ata_port *ap);
+extern int sata_async_notification(struct ata_port *ap);
 
 extern void ata_eh_freeze_port(struct ata_port *ap);
 extern void ata_eh_thaw_port(struct ata_port *ap);
-- 
1.5.0.3



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

* [PATCH 10/12] libata-pmp-prep: implement EH fast-fail path
  2007-09-23  4:14 [PATCHSET 1/2] libata: prep for PMP support, take 6 Tejun Heo
                   ` (8 preceding siblings ...)
  2007-09-23  4:14 ` [PATCH 12/12] libata-pmp-prep: implement sata_async_notification() Tejun Heo
@ 2007-09-23  4:14 ` Tejun Heo
  2007-09-23  4:14 ` [PATCH 09/12] libata-pmp-prep: implement ATA_LFLAG_DISABLED Tejun Heo
  2007-09-23  4:14 ` [PATCH 11/12] libata-pmp-prep: implement ATA_HORKAGE_SKIP_PM Tejun Heo
  11 siblings, 0 replies; 15+ messages in thread
From: Tejun Heo @ 2007-09-23  4:14 UTC (permalink / raw)
  To: jeff, alan, linux-ide; +Cc: Tejun Heo

If PMP itself becomes inaccessible while trying to link a downstream
link, spending time to recover the downstream link doesn't make any
sense.  Make EH skip retry and fail fast if -ERESTART is received.

Signed-off-by: Tejun Heo <htejun@gmail.com>
---
 drivers/ata/libata-eh.c |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c
index fbbf791..3c31e10 100644
--- a/drivers/ata/libata-eh.c
+++ b/drivers/ata/libata-eh.c
@@ -2050,7 +2050,7 @@ int ata_eh_reset(struct ata_link *link, int classify,
 	if (rc == -EAGAIN)
 		rc = 0;
 
-	if (rc && try < ARRAY_SIZE(ata_eh_reset_timeouts)) {
+	if (rc && rc != -ERESTART && try < ARRAY_SIZE(ata_eh_reset_timeouts)) {
 		unsigned long now = jiffies;
 
 		if (time_before(now, deadline)) {
-- 
1.5.0.3



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

* Re: [PATCH 01/12] libata: misc updates for AN
  2007-09-23  4:14 ` [PATCH 01/12] libata: misc updates for AN Tejun Heo
@ 2007-09-26  2:27   ` Jeff Garzik
  0 siblings, 0 replies; 15+ messages in thread
From: Jeff Garzik @ 2007-09-26  2:27 UTC (permalink / raw)
  To: Tejun Heo; +Cc: alan, linux-ide

Tejun Heo wrote:
> Update AN support in preparation of PMP support.
> 
> * s/ata_id_has_AN/ata_id_has_atapi_AN/
> * add AN enabled reporting during configuration
> * add err_mask to AN configuration failure reporting
> * update LOCKING comment for ata_scsi_media_change_notify()
> * check whether ATA dev is attached to SCSI dev ata_scsi_media_change_notify()
> * set ATA_FLAG_AN in ahci and sata_sil24
> 
> Signed-off-by: Tejun Heo <htejun@gmail.com>
> Cc: Kriten Carlson Accardi <kristen.c.accardi@intel.com>
> ---
>  drivers/ata/ahci.c        |    2 +-
>  drivers/ata/libata-core.c |   24 ++++++++++++++----------
>  drivers/ata/libata-scsi.c |    7 ++++---
>  drivers/ata/sata_sil24.c  |   13 +++++++++++--
>  include/linux/ata.h       |    2 +-
>  5 files changed, 31 insertions(+), 17 deletions(-)

applied 1-12



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

end of thread, other threads:[~2007-09-26  2:27 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2007-09-23  4:14 [PATCHSET 1/2] libata: prep for PMP support, take 6 Tejun Heo
2007-09-23  4:14 ` [PATCH 01/12] libata: misc updates for AN Tejun Heo
2007-09-26  2:27   ` Jeff Garzik
2007-09-23  4:14 ` [PATCH 08/12] libata-pmp-prep: implement ATA_LFLAG_NO_RETRY Tejun Heo
2007-09-23  4:14 ` [PATCH 05/12] libata-pmp-prep: implement ops->qc_defer() Tejun Heo
2007-09-23  4:14 ` [PATCH 04/12] libata-pmp-prep: make a number of functions global to libata Tejun Heo
2007-09-23  4:14 ` [PATCH 07/12] libata-pmp-prep: implement ATA_LFLAG_NO_SRST, ASSUME_ATA and ASSUME_SEMB Tejun Heo
2007-09-23  4:14 ` [PATCH 06/12] libata-pmp-prep: implement qc_defer helpers Tejun Heo
2007-09-23  4:14 ` [PATCH 03/12] libata-pmp-prep: add @new_class to ata_dev_revalidate() Tejun Heo
2007-09-23  4:14 ` [PATCH 02/12] libata-pmp-prep: add PMP related constants, fields, ops and update helpers Tejun Heo
2007-09-23  4:14 ` [PATCH 12/12] libata-pmp-prep: implement sata_async_notification() Tejun Heo
2007-09-23  4:14 ` [PATCH 10/12] libata-pmp-prep: implement EH fast-fail path Tejun Heo
2007-09-23  4:14 ` [PATCH 09/12] libata-pmp-prep: implement ATA_LFLAG_DISABLED Tejun Heo
2007-09-23  4:14 ` [PATCH 11/12] libata-pmp-prep: implement ATA_HORKAGE_SKIP_PM Tejun Heo
  -- strict thread matches above, loose matches on Subject: below --
2007-07-01 10:26 [PATCHSET 3/4] libata: prep for PMP support, take 4 Tejun Heo
2007-07-01 10:26 ` [PATCH 06/12] libata-pmp-prep: implement qc_defer helpers Tejun Heo

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.