| From 09c5b4803a80a5451d950d6a539d2eb311dc0fb1 Mon Sep 17 00:00:00 2001 |
| From: Gabriele Mazzotta <gabriele.mzt@gmail.com> |
| Date: Sat, 25 Apr 2015 19:52:37 +0200 |
| Subject: libata: Ignore spurious PHY event on LPM policy change |
| |
| From: Gabriele Mazzotta <gabriele.mzt@gmail.com> |
| |
| commit 09c5b4803a80a5451d950d6a539d2eb311dc0fb1 upstream. |
| |
| When the LPM policy is set to ATA_LPM_MAX_POWER, the device might |
| generate a spurious PHY event that cuases errors on the link. |
| Ignore this event if it occured 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. |
| |
| Link: http://lkml.kernel.org/g/3352987.ugV1Ipy7Z5@xps13 |
| Signed-off-by: Gabriele Mazzotta <gabriele.mzt@gmail.com> |
| Signed-off-by: Tejun Heo <tj@kernel.org> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| drivers/ata/libata-core.c | 15 ++++++++++++++- |
| drivers/ata/libata-eh.c | 3 +++ |
| include/linux/libata.h | 9 +++++++++ |
| 3 files changed, 26 insertions(+), 1 deletion(-) |
| |
| --- a/drivers/ata/libata-core.c |
| +++ b/drivers/ata/libata-core.c |
| @@ -6839,8 +6839,21 @@ u32 ata_wait_register(struct ata_port *a |
| */ |
| bool sata_lpm_ignore_phy_events(struct ata_link *link) |
| { |
| + unsigned long lpm_timeout = link->last_lpm_change + |
| + msecs_to_jiffies(ATA_TMOUT_SPURIOUS_PHY); |
| + |
| /* if LPM is enabled, PHYRDY doesn't mean anything */ |
| - return !!(link->lpm_policy > ATA_LPM_MAX_POWER); |
| + if (link->lpm_policy > ATA_LPM_MAX_POWER) |
| + return true; |
| + |
| + /* ignore the first PHY event after the LPM policy changed |
| + * as it is might be spurious |
| + */ |
| + if ((link->flags & ATA_LFLAG_CHANGED) && |
| + time_before(jiffies, lpm_timeout)) |
| + return true; |
| + |
| + return false; |
| } |
| EXPORT_SYMBOL_GPL(sata_lpm_ignore_phy_events); |
| |
| --- a/drivers/ata/libata-eh.c |
| +++ b/drivers/ata/libata-eh.c |
| @@ -3488,6 +3488,9 @@ static int ata_eh_set_lpm(struct ata_lin |
| } |
| } |
| |
| + link->last_lpm_change = jiffies; |
| + link->flags |= ATA_LFLAG_CHANGED; |
| + |
| return 0; |
| |
| fail: |
| --- a/include/linux/libata.h |
| +++ b/include/linux/libata.h |
| @@ -204,6 +204,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 */ |
| @@ -307,6 +308,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, |
| @@ -785,6 +792,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]) |