Commit e8d19999 authored by Gabriele Mazzotta's avatar Gabriele Mazzotta Committed by Greg Kroah-Hartman

libata: Ignore spurious PHY event on LPM policy change

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@xps13Signed-off-by: default avatarGabriele Mazzotta <gabriele.mzt@gmail.com>
Signed-off-by: default avatarTejun Heo <tj@kernel.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 8d850bbf
...@@ -6814,8 +6814,21 @@ u32 ata_wait_register(struct ata_port *ap, void __iomem *reg, u32 mask, u32 val, ...@@ -6814,8 +6814,21 @@ u32 ata_wait_register(struct ata_port *ap, void __iomem *reg, u32 mask, u32 val,
*/ */
bool sata_lpm_ignore_phy_events(struct ata_link *link) 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 */ /* 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); EXPORT_SYMBOL_GPL(sata_lpm_ignore_phy_events);
......
...@@ -3481,6 +3481,9 @@ static int ata_eh_set_lpm(struct ata_link *link, enum ata_lpm_policy policy, ...@@ -3481,6 +3481,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; return 0;
fail: fail:
......
...@@ -187,6 +187,7 @@ enum { ...@@ -187,6 +187,7 @@ enum {
ATA_LFLAG_SW_ACTIVITY = (1 << 7), /* keep activity stats */ ATA_LFLAG_SW_ACTIVITY = (1 << 7), /* keep activity stats */
ATA_LFLAG_NO_LPM = (1 << 8), /* disable LPM on this link */ ATA_LFLAG_NO_LPM = (1 << 8), /* disable LPM on this link */
ATA_LFLAG_RST_ONCE = (1 << 9), /* limit recovery to one reset */ 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 */ /* struct ata_port flags */
ATA_FLAG_SLAVE_POSS = (1 << 0), /* host supports slave dev */ ATA_FLAG_SLAVE_POSS = (1 << 0), /* host supports slave dev */
...@@ -289,6 +290,12 @@ enum { ...@@ -289,6 +290,12 @@ enum {
*/ */
ATA_TMOUT_PMP_SRST_WAIT = 5000, 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 */ /* ATA bus states */
BUS_UNKNOWN = 0, BUS_UNKNOWN = 0,
BUS_DMA = 1, BUS_DMA = 1,
...@@ -736,6 +743,8 @@ struct ata_link { ...@@ -736,6 +743,8 @@ struct ata_link {
struct ata_eh_context eh_context; struct ata_eh_context eh_context;
struct ata_device device[ATA_MAX_DEVICES]; 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_BEGIN offsetof(struct ata_link, active_tag)
#define ATA_LINK_CLEAR_END offsetof(struct ata_link, device[0]) #define ATA_LINK_CLEAR_END offsetof(struct ata_link, device[0])
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment