* [PATCHSET 3/4] libata: prep for PMP support, take 5
@ 2007-07-16 9:39 Tejun Heo
2007-07-16 9:39 ` [PATCH 02/11] libata-pmp-prep: add @new_class to ata_dev_revalidate() Tejun Heo
` (10 more replies)
0 siblings, 11 replies; 12+ messages in thread
From: Tejun Heo @ 2007-07-16 9:39 UTC (permalink / raw)
To: Jeff Garzik, Alan Cox, linux-ide, Forrest Zhao, htejun
Hello, all.
This is the fifth take of libata-pmp-prep patchset. This patchset
contains 12 patches implementing various stuff needed for PMP.
#01 : add PMP related constants
#02-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 : SATA SDB notify handler
This patchset hasn't changed from the last take[L] other than being
regenerated.
This patchset is against
libata-dev#upstream (d79eb6de54f180dc83b488b20747f0097fde3dea)
+ [1] misc-updates patchset
+ [2] libata-link patchset
Thanks.
--
tejun
[L] http://thread.gmane.org/gmane.linux.ide/20115
[1] http://thread.gmane.org/gmane.linux.ide/20824
[2] http://thread.gmane.org/gmane.linux.ide/20842
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH 02/11] libata-pmp-prep: add @new_class to ata_dev_revalidate()
2007-07-16 9:39 [PATCHSET 3/4] libata: prep for PMP support, take 5 Tejun Heo
@ 2007-07-16 9:39 ` Tejun Heo
2007-07-16 9:39 ` [PATCH 03/11] libata-pmp-prep: make a number of functions global to libata Tejun Heo
` (9 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Tejun Heo @ 2007-07-16 9:39 UTC (permalink / raw)
To: Jeff Garzik, Alan Cox, linux-ide, Forrest Zhao; +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 19e831a..684996a 100644
--- a/drivers/ata/libata-core.c
+++ b/drivers/ata/libata-core.c
@@ -2766,7 +2766,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;
@@ -3668,6 +3668,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
@@ -3679,7 +3680,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;
@@ -3687,6 +3689,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 2ddc2ed..b91f413 100644
--- a/drivers/ata/libata-eh.c
+++ b/drivers/ata/libata-eh.c
@@ -2031,7 +2031,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 6d85ede..4153817 100644
--- a/drivers/ata/libata.h
+++ b/drivers/ata/libata.h
@@ -76,7 +76,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] 12+ messages in thread
* [PATCH 03/11] libata-pmp-prep: make a number of functions global to libata
2007-07-16 9:39 [PATCHSET 3/4] libata: prep for PMP support, take 5 Tejun Heo
2007-07-16 9:39 ` [PATCH 02/11] libata-pmp-prep: add @new_class to ata_dev_revalidate() Tejun Heo
@ 2007-07-16 9:39 ` Tejun Heo
2007-07-16 9:39 ` [PATCH 01/11] libata-pmp-prep: add PMP related constants, fields, ops and update helpers Tejun Heo
` (8 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Tejun Heo @ 2007-07-16 9:39 UTC (permalink / raw)
To: Jeff Garzik, Alan Cox, linux-ide, Forrest Zhao; +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 684996a..22e508e 100644
--- a/drivers/ata/libata-core.c
+++ b/drivers/ata/libata-core.c
@@ -6063,7 +6063,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;
@@ -6098,7 +6098,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 b91f413..0eab780 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);
@@ -948,7 +947,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;
@@ -985,8 +984,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;
@@ -1028,8 +1027,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;
@@ -1689,7 +1688,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;
@@ -1794,7 +1793,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;
@@ -1845,9 +1844,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;
@@ -2223,10 +2222,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;
@@ -2372,7 +2371,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 4153817..fe09aa8 100644
--- a/drivers/ata/libata.h
+++ b/drivers/ata/libata.h
@@ -93,6 +93,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);
@@ -154,6 +156,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] 12+ messages in thread
* [PATCH 01/11] libata-pmp-prep: add PMP related constants, fields, ops and update helpers
2007-07-16 9:39 [PATCHSET 3/4] libata: prep for PMP support, take 5 Tejun Heo
2007-07-16 9:39 ` [PATCH 02/11] libata-pmp-prep: add @new_class to ata_dev_revalidate() Tejun Heo
2007-07-16 9:39 ` [PATCH 03/11] libata-pmp-prep: make a number of functions global to libata Tejun Heo
@ 2007-07-16 9:39 ` Tejun Heo
2007-07-16 9:39 ` [PATCH 07/11] libata-pmp-prep: implement ATA_LFLAG_NO_RETRY Tejun Heo
` (7 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Tejun Heo @ 2007-07-16 9:39 UTC (permalink / raw)
To: Jeff Garzik, Alan Cox, linux-ide, Forrest Zhao; +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 | 31 +++++++++++++++++++++++++++----
1 files changed, 27 insertions(+), 4 deletions(-)
diff --git a/include/linux/libata.h b/include/linux/libata.h
index 06e1ead..ac9f5f5 100644
--- a/include/linux/libata.h
+++ b/include/linux/libata.h
@@ -154,7 +154,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 */
@@ -179,6 +183,8 @@ enum {
ATA_FLAG_IGN_SIMPLEX = (1 << 15), /* ignore SIMPLEX */
ATA_FLAG_NO_IORDY = (1 << 16), /* controller lacks iordy */
ATA_FLAG_ACPI_SATA = (1 << 17), /* need native SATA ACPI layout */
+ ATA_FLAG_PMP = (1 << 18),
+ ATA_FLAG_SDB_NOTIFY = (1 << 19),
/* The following flag belongs to ap->pflags but is kept in
* ap->flags because it's referenced in many LLDs and will be
@@ -295,6 +301,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 */
@@ -446,7 +456,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;
@@ -625,6 +640,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.
*/
@@ -1014,12 +1035,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] 12+ messages in thread
* [PATCH 04/11] libata-pmp-prep: implement ops->qc_defer()
2007-07-16 9:39 [PATCHSET 3/4] libata: prep for PMP support, take 5 Tejun Heo
` (5 preceding siblings ...)
2007-07-16 9:39 ` [PATCH 05/11] libata-pmp-prep: implement qc_defer helpers Tejun Heo
@ 2007-07-16 9:39 ` Tejun Heo
2007-07-16 9:39 ` [PATCH 09/11] libata-pmp-prep: implement EH fast-fail path Tejun Heo
` (3 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Tejun Heo @ 2007-07-16 9:39 UTC (permalink / raw)
To: Jeff Garzik, Alan Cox, linux-ide, Forrest Zhao; +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 8c1cad9..805887e 100644
--- a/drivers/ata/ahci.c
+++ b/drivers/ata/ahci.c
@@ -270,6 +270,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,
@@ -304,6 +305,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 22e508e..48c5780 100644
--- a/drivers/ata/libata-core.c
+++ b/drivers/ata/libata-core.c
@@ -4196,6 +4196,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
*
@@ -6966,6 +6996,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 c3c9840..1a1c018 100644
--- a/drivers/ata/libata-scsi.c
+++ b/drivers/ata/libata-scsi.c
@@ -755,6 +755,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,
@@ -1422,37 +1429,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
@@ -1483,14 +1459,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;
@@ -1518,6 +1492,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);
@@ -1539,8 +1518,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;
}
/**
@@ -2943,6 +2926,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 1f71946..262e291 100644
--- a/drivers/ata/sata_nv.c
+++ b/drivers/ata/sata_nv.c
@@ -430,6 +430,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 8d89053..480ce87 100644
--- a/drivers/ata/sata_sil24.c
+++ b/drivers/ata/sata_sil24.c
@@ -394,6 +394,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 ac9f5f5..6c0628d 100644
--- a/include/linux/libata.h
+++ b/include/linux/libata.h
@@ -269,6 +269,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,
@@ -637,6 +641,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);
@@ -821,6 +826,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] 12+ messages in thread
* [PATCH 05/11] libata-pmp-prep: implement qc_defer helpers
2007-07-16 9:39 [PATCHSET 3/4] libata: prep for PMP support, take 5 Tejun Heo
` (4 preceding siblings ...)
2007-07-16 9:39 ` [PATCH 06/11] libata-pmp-prep: implement ATA_LFLAG_NO_SRST, ASSUME_ATA and ASSUME_SEMB Tejun Heo
@ 2007-07-16 9:39 ` Tejun Heo
2007-07-16 9:39 ` [PATCH 04/11] libata-pmp-prep: implement ops->qc_defer() Tejun Heo
` (4 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Tejun Heo @ 2007-07-16 9:39 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 48c5780..76e0197 100644
--- a/drivers/ata/libata-core.c
+++ b/drivers/ata/libata-core.c
@@ -1352,6 +1352,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;
@@ -1390,9 +1391,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;
@@ -1471,6 +1474,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
@@ -5259,10 +5263,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
@@ -5441,9 +5454,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 0eab780..93f023c 100644
--- a/drivers/ata/libata-eh.c
+++ b/drivers/ata/libata-eh.c
@@ -381,6 +381,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);
@@ -2401,6 +2402,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 6c0628d..3b8ce64 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;
@@ -1086,6 +1089,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] 12+ messages in thread
* [PATCH 07/11] libata-pmp-prep: implement ATA_LFLAG_NO_RETRY
2007-07-16 9:39 [PATCHSET 3/4] libata: prep for PMP support, take 5 Tejun Heo
` (2 preceding siblings ...)
2007-07-16 9:39 ` [PATCH 01/11] libata-pmp-prep: add PMP related constants, fields, ops and update helpers Tejun Heo
@ 2007-07-16 9:39 ` Tejun Heo
2007-07-16 9:39 ` [PATCH 06/11] libata-pmp-prep: implement ATA_LFLAG_NO_SRST, ASSUME_ATA and ASSUME_SEMB Tejun Heo
` (6 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Tejun Heo @ 2007-07-16 9:39 UTC (permalink / raw)
To: Jeff Garzik, Alan Cox, linux-ide, Forrest Zhao; +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 e5f644e..b99e70b 100644
--- a/drivers/ata/libata-eh.c
+++ b/drivers/ata/libata-eh.c
@@ -2262,7 +2262,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 3d84c2b..787cebc 100644
--- a/include/linux/libata.h
+++ b/include/linux/libata.h
@@ -168,6 +168,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] 12+ messages in thread
* [PATCH 06/11] libata-pmp-prep: implement ATA_LFLAG_NO_SRST, ASSUME_ATA and ASSUME_SEMB
2007-07-16 9:39 [PATCHSET 3/4] libata: prep for PMP support, take 5 Tejun Heo
` (3 preceding siblings ...)
2007-07-16 9:39 ` [PATCH 07/11] libata-pmp-prep: implement ATA_LFLAG_NO_RETRY Tejun Heo
@ 2007-07-16 9:39 ` Tejun Heo
2007-07-16 9:39 ` [PATCH 05/11] libata-pmp-prep: implement qc_defer helpers Tejun Heo
` (5 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Tejun Heo @ 2007-07-16 9:39 UTC (permalink / raw)
To: Jeff Garzik, Alan Cox, linux-ide, Forrest Zhao; +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 93f023c..e5f644e 100644
--- a/drivers/ata/libata-eh.c
+++ b/drivers/ata/libata-eh.c
@@ -1833,14 +1833,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;
}
@@ -1867,7 +1871,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
@@ -1930,7 +1935,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;
@@ -1945,8 +1950,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;
@@ -1954,6 +1959,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;
@@ -1978,12 +1987,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 3b8ce64..3d84c2b 100644
--- a/include/linux/libata.h
+++ b/include/linux/libata.h
@@ -164,6 +164,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] 12+ messages in thread
* [PATCH 09/11] libata-pmp-prep: implement EH fast-fail path
2007-07-16 9:39 [PATCHSET 3/4] libata: prep for PMP support, take 5 Tejun Heo
` (6 preceding siblings ...)
2007-07-16 9:39 ` [PATCH 04/11] libata-pmp-prep: implement ops->qc_defer() Tejun Heo
@ 2007-07-16 9:39 ` Tejun Heo
2007-07-16 9:39 ` [PATCH 08/11] libata-pmp-prep: implement ATA_LFLAG_DISABLED Tejun Heo
` (2 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Tejun Heo @ 2007-07-16 9:39 UTC (permalink / raw)
To: Jeff Garzik, Alan Cox, linux-ide, Forrest Zhao; +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 c250d6d..7bd8d06 100644
--- a/drivers/ata/libata-eh.c
+++ b/drivers/ata/libata-eh.c
@@ -1977,7 +1977,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] 12+ messages in thread
* [PATCH 10/11] libata-pmp-prep: implement ATA_HORKAGE_SKIP_PM
2007-07-16 9:39 [PATCHSET 3/4] libata: prep for PMP support, take 5 Tejun Heo
` (8 preceding siblings ...)
2007-07-16 9:39 ` [PATCH 08/11] libata-pmp-prep: implement ATA_LFLAG_DISABLED Tejun Heo
@ 2007-07-16 9:39 ` Tejun Heo
2007-07-16 9:39 ` [PATCH 11/11] libata-pmp-prep: implement sata_async_notification() Tejun Heo
10 siblings, 0 replies; 12+ messages in thread
From: Tejun Heo @ 2007-07-16 9:39 UTC (permalink / raw)
To: Jeff Garzik, Alan Cox, linux-ide, Forrest Zhao; +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 1a1c018..3b8693f 100644
--- a/drivers/ata/libata-scsi.c
+++ b/drivers/ata/libata-scsi.c
@@ -950,6 +950,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 717fdb5..4e37180 100644
--- a/include/linux/libata.h
+++ b/include/linux/libata.h
@@ -325,6 +325,7 @@ enum {
ATA_HORKAGE_NODMA = (1 << 1), /* DMA problems */
ATA_HORKAGE_NONCQ = (1 << 2), /* Don't use NCQ */
ATA_HORKAGE_MAX_SEC_128 = (1 << 3), /* Limit max sects to 128 */
+ ATA_HORKAGE_SKIP_PM = (1 << 4), /* Skip PM operations */
};
enum hsm_task_states {
--
1.5.0.3
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH 08/11] libata-pmp-prep: implement ATA_LFLAG_DISABLED
2007-07-16 9:39 [PATCHSET 3/4] libata: prep for PMP support, take 5 Tejun Heo
` (7 preceding siblings ...)
2007-07-16 9:39 ` [PATCH 09/11] libata-pmp-prep: implement EH fast-fail path Tejun Heo
@ 2007-07-16 9:39 ` Tejun Heo
2007-07-16 9:39 ` [PATCH 10/11] libata-pmp-prep: implement ATA_HORKAGE_SKIP_PM Tejun Heo
2007-07-16 9:39 ` [PATCH 11/11] libata-pmp-prep: implement sata_async_notification() Tejun Heo
10 siblings, 0 replies; 12+ messages in thread
From: Tejun Heo @ 2007-07-16 9:39 UTC (permalink / raw)
To: Jeff Garzik, Alan Cox, linux-ide, Forrest Zhao; +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 b99e70b..c250d6d 100644
--- a/drivers/ata/libata-eh.c
+++ b/drivers/ata/libata-eh.c
@@ -1241,6 +1241,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;
@@ -1259,7 +1260,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;
@@ -2154,6 +2168,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))
@@ -2254,6 +2272,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");
@@ -2261,6 +2280,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 787cebc..717fdb5 100644
--- a/include/linux/libata.h
+++ b/include/linux/libata.h
@@ -169,6 +169,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 */
@@ -286,6 +287,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,
@@ -991,6 +993,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] 12+ messages in thread
* [PATCH 11/11] libata-pmp-prep: implement sata_async_notification()
2007-07-16 9:39 [PATCHSET 3/4] libata: prep for PMP support, take 5 Tejun Heo
` (9 preceding siblings ...)
2007-07-16 9:39 ` [PATCH 10/11] libata-pmp-prep: implement ATA_HORKAGE_SKIP_PM Tejun Heo
@ 2007-07-16 9:39 ` Tejun Heo
10 siblings, 0 replies; 12+ messages in thread
From: Tejun Heo @ 2007-07-16 9:39 UTC (permalink / raw)
To: Jeff Garzik, Alan Cox, linux-ide, Forrest Zhao; +Cc: Tejun Heo
Implement SATA async notification handler sata_async_notification().
LLDs can call this function when it suspects SDB_NOTIFY has occurred.
This function will check SCR_NOTIFICATION if available and schedule
proper EH action.
Currently, only PMP notification is handled. The function can be
easily extended to cover ATAPI notification later.
Signed-off-by: Tejun Heo <htejun@gmail.com>
---
drivers/ata/libata-core.c | 1 +
drivers/ata/libata-eh.c | 49 +++++++++++++++++++++++++++++++++++++++++++++
include/linux/libata.h | 1 +
3 files changed, 51 insertions(+), 0 deletions(-)
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
index 76e0197..438e524 100644
--- a/drivers/ata/libata-core.c
+++ b/drivers/ata/libata-core.c
@@ -7099,6 +7099,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 7bd8d06..3cfa860 100644
--- a/drivers/ata/libata-eh.c
+++ b/drivers/ata/libata-eh.c
@@ -838,6 +838,55 @@ 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_SDB_NOTIFY))
+ return 0;
+
+ rc = sata_scr_read(&ap->link, SCR_NOTIFICATION, &sntf);
+ if (rc == 0) {
+ if (!sntf)
+ return 0;
+
+ /* clear it */
+ sata_scr_write(&ap->link, SCR_NOTIFICATION, sntf);
+
+ /* PMP is reporting that PHY status of some downstream
+ * ports have changed. Schedule EH.
+ */
+ if (ap->nr_pmp_links && (sntf & (1 << SATA_PMP_CTRL_PORT))) {
+ ata_port_schedule_eh(ap);
+ return 1;
+ }
+ } else {
+ /* Dunno what's going on. Schedule EH if PMP is
+ * attached. It might be reporting PHY status change.
+ */
+ if (ap->nr_pmp_links) {
+ 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/include/linux/libata.h b/include/linux/libata.h
index 4e37180..989744a 100644
--- a/include/linux/libata.h
+++ b/include/linux/libata.h
@@ -947,6 +947,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] 12+ messages in thread
end of thread, other threads:[~2007-07-16 9:39 UTC | newest]
Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2007-07-16 9:39 [PATCHSET 3/4] libata: prep for PMP support, take 5 Tejun Heo
2007-07-16 9:39 ` [PATCH 02/11] libata-pmp-prep: add @new_class to ata_dev_revalidate() Tejun Heo
2007-07-16 9:39 ` [PATCH 03/11] libata-pmp-prep: make a number of functions global to libata Tejun Heo
2007-07-16 9:39 ` [PATCH 01/11] libata-pmp-prep: add PMP related constants, fields, ops and update helpers Tejun Heo
2007-07-16 9:39 ` [PATCH 07/11] libata-pmp-prep: implement ATA_LFLAG_NO_RETRY Tejun Heo
2007-07-16 9:39 ` [PATCH 06/11] libata-pmp-prep: implement ATA_LFLAG_NO_SRST, ASSUME_ATA and ASSUME_SEMB Tejun Heo
2007-07-16 9:39 ` [PATCH 05/11] libata-pmp-prep: implement qc_defer helpers Tejun Heo
2007-07-16 9:39 ` [PATCH 04/11] libata-pmp-prep: implement ops->qc_defer() Tejun Heo
2007-07-16 9:39 ` [PATCH 09/11] libata-pmp-prep: implement EH fast-fail path Tejun Heo
2007-07-16 9:39 ` [PATCH 08/11] libata-pmp-prep: implement ATA_LFLAG_DISABLED Tejun Heo
2007-07-16 9:39 ` [PATCH 10/11] libata-pmp-prep: implement ATA_HORKAGE_SKIP_PM Tejun Heo
2007-07-16 9:39 ` [PATCH 11/11] libata-pmp-prep: implement sata_async_notification() 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).