diff mbox

libata: Ignore spurious PHY events on LPM policy change

Message ID 1429954469-27892-1-git-send-email-gabriele.mzt@gmail.com
State Not Applicable
Delegated to: David Miller
Headers show

Commit Message

Gabriele Mazzotta April 25, 2015, 9:34 a.m. UTC
When the LPM policy is set to ATA_LPM_MAX_POWER, the device might
generate a spurious PHY event that might cause errors on the link.
Ignore this event if it occurred within 10s after the policy change.

The timeout was chosen observing that on a Dell XPS13 9333 these
spurious events can occur up to roughly 6s after the policy change.

Signed-off-by: Gabriele Mazzotta <gabriele.mzt@gmail.com>
---
 drivers/ata/libahci.c   | 13 ++++++++++++-
 drivers/ata/libata-eh.c |  3 +++
 include/linux/libata.h  |  9 +++++++++
 3 files changed, 24 insertions(+), 1 deletion(-)

Comments

Tejun Heo April 25, 2015, 3:37 p.m. UTC | #1
On Sat, Apr 25, 2015 at 11:34:29AM +0200, Gabriele Mazzotta wrote:
> When the LPM policy is set to ATA_LPM_MAX_POWER, the device might
> generate a spurious PHY event that might cause errors on the link.
> Ignore this event if it occurred within 10s after the policy change.
> 
> The timeout was chosen observing that on a Dell XPS13 9333 these
> spurious events can occur up to roughly 6s after the policy change.

Just a couple things.

Can you please add the following tag?

Link: http://lkml.kernel.org/g/<MSG-ID of the first message in this thread>

> diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c
> index 61a9c07..59a2517 100644
> --- a/drivers/ata/libahci.c
> +++ b/drivers/ata/libahci.c
> @@ -1700,6 +1700,9 @@ static void ahci_handle_port_interrupt(struct ata_port *ap,
>  	struct ahci_port_priv *pp = ap->private_data;
>  	struct ahci_host_priv *hpriv = ap->host->private_data;
>  	int resetting = !!(ap->pflags & ATA_PFLAG_RESETTING);
> +	unsigned long lpm_timeout = ap->link.last_lpm_change +
> +				    msecs_to_jiffies(ATA_TMOUT_SPURIOUS_PHY);
> +	bool ignore_event = false;
>  	u32 qc_active = 0;
>  	int rc;
>  
> @@ -1707,8 +1710,16 @@ static void ahci_handle_port_interrupt(struct ata_port *ap,
>  	if (unlikely(resetting))
>  		status &= ~PORT_IRQ_BAD_PMP;
>  
> +	/* ignore the first PHY event after the LPM policy changed
> +	 * as it is might be spurious
> +	 */
> +	if ((ap->link.flags & ATA_LFLAG_CHANGED) &&
> +	    time_before(jiffies, lpm_timeout))
> +		ignore_event = true;

Maybe the following is better?

	ignore_event = (ap->link.flags & ATA_LFLAG_CHANGED) &&
		       time_before(jiffies, lpm_timeout);

> +
>  	/* if LPM is enabled, PHYRDY doesn't mean anything */
> -	if (ap->link.lpm_policy > ATA_LPM_MAX_POWER) {
> +	if (ap->link.lpm_policy > ATA_LPM_MAX_POWER || ignore_event) {
> +		ap->link.flags &= ~ATA_LFLAG_CHANGED;
>  		status &= ~PORT_IRQ_PHYRDY;
>  		ahci_scr_write(&ap->link, SCR_ERROR, SERR_PHYRDY_CHG);
>  	}

I thought more about it and it's weird to have ATA_LFLAG_CHANGED and
last_lpm_change timestamp in libata core side and then open-code the
actual logic in ahci.  How about implementing a generic helper, say,
bool sata_lpm_ignore_phy_events(link) in libata core and use it in
specific drivers?  It can test both the current lpm_policy and the
timeout (preferably a separate patch to introduce the function and
factor out lpm policy testing).

Thanks.
Gabriele Mazzotta April 25, 2015, 5:57 p.m. UTC | #2
On Saturday 25 April 2015 11:37:51 Tejun Heo wrote:
> I thought more about it and it's weird to have ATA_LFLAG_CHANGED and
> last_lpm_change timestamp in libata core side and then open-code the
> actual logic in ahci.  How about implementing a generic helper, say,
> bool sata_lpm_ignore_phy_events(link) in libata core and use it in
> specific drivers?  It can test both the current lpm_policy and the
> timeout (preferably a separate patch to introduce the function and
> factor out lpm policy testing).

I agree. I'm now going to send the revised patches, thanks for all
the suggestions.

Gabriele

--
To unsubscribe from this list: send the line "unsubscribe linux-ide" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c
index 61a9c07..59a2517 100644
--- a/drivers/ata/libahci.c
+++ b/drivers/ata/libahci.c
@@ -1700,6 +1700,9 @@  static void ahci_handle_port_interrupt(struct ata_port *ap,
 	struct ahci_port_priv *pp = ap->private_data;
 	struct ahci_host_priv *hpriv = ap->host->private_data;
 	int resetting = !!(ap->pflags & ATA_PFLAG_RESETTING);
+	unsigned long lpm_timeout = ap->link.last_lpm_change +
+				    msecs_to_jiffies(ATA_TMOUT_SPURIOUS_PHY);
+	bool ignore_event = false;
 	u32 qc_active = 0;
 	int rc;
 
@@ -1707,8 +1710,16 @@  static void ahci_handle_port_interrupt(struct ata_port *ap,
 	if (unlikely(resetting))
 		status &= ~PORT_IRQ_BAD_PMP;
 
+	/* ignore the first PHY event after the LPM policy changed
+	 * as it is might be spurious
+	 */
+	if ((ap->link.flags & ATA_LFLAG_CHANGED) &&
+	    time_before(jiffies, lpm_timeout))
+		ignore_event = true;
+
 	/* if LPM is enabled, PHYRDY doesn't mean anything */
-	if (ap->link.lpm_policy > ATA_LPM_MAX_POWER) {
+	if (ap->link.lpm_policy > ATA_LPM_MAX_POWER || ignore_event) {
+		ap->link.flags &= ~ATA_LFLAG_CHANGED;
 		status &= ~PORT_IRQ_PHYRDY;
 		ahci_scr_write(&ap->link, SCR_ERROR, SERR_PHYRDY_CHG);
 	}
diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c
index 07f41be..cf0022e 100644
--- a/drivers/ata/libata-eh.c
+++ b/drivers/ata/libata-eh.c
@@ -3597,6 +3597,9 @@  static int ata_eh_set_lpm(struct ata_link *link, enum ata_lpm_policy policy,
 		}
 	}
 
+	link->last_lpm_change = jiffies;
+	link->flags |= ATA_LFLAG_CHANGED;
+
 	return 0;
 
 fail:
diff --git a/include/linux/libata.h b/include/linux/libata.h
index 8dad4a3..05dbff3 100644
--- a/include/linux/libata.h
+++ b/include/linux/libata.h
@@ -205,6 +205,7 @@  enum {
 	ATA_LFLAG_SW_ACTIVITY	= (1 << 7), /* keep activity stats */
 	ATA_LFLAG_NO_LPM	= (1 << 8), /* disable LPM on this link */
 	ATA_LFLAG_RST_ONCE	= (1 << 9), /* limit recovery to one reset */
+	ATA_LFLAG_CHANGED	= (1 << 10), /* LPM state changed on this link */
 
 	/* struct ata_port flags */
 	ATA_FLAG_SLAVE_POSS	= (1 << 0), /* host supports slave dev */
@@ -309,6 +310,12 @@  enum {
 	 */
 	ATA_TMOUT_PMP_SRST_WAIT	= 5000,
 
+	/* When the LPM policy is set to ATA_LPM_MAX_POWER, there might
+	 * be a spurious PHY event, so ignore the first PHY event that
+	 * occurs within 10s after the policy change.
+	 */
+	ATA_TMOUT_SPURIOUS_PHY	= 10000,
+
 	/* ATA bus states */
 	BUS_UNKNOWN		= 0,
 	BUS_DMA			= 1,
@@ -788,6 +795,8 @@  struct ata_link {
 	struct ata_eh_context	eh_context;
 
 	struct ata_device	device[ATA_MAX_DEVICES];
+
+	unsigned long		last_lpm_change; /* when last LPM change happened */
 };
 #define ATA_LINK_CLEAR_BEGIN		offsetof(struct ata_link, active_tag)
 #define ATA_LINK_CLEAR_END		offsetof(struct ata_link, device[0])