libata: Ignore spurious PHY event on LPM policy change
authorGabriele Mazzotta <gabriele.mzt@gmail.com>
Sat, 25 Apr 2015 17:52:37 +0000 (19:52 +0200)
committerBen Hutchings <ben@decadent.org.uk>
Thu, 6 Aug 2015 23:32:06 +0000 (00:32 +0100)
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>
[bwh: Backported to 3.2: adjust context]
Signed-off-by: Ben Hutchings <ben@decadent.org.uk>
drivers/ata/libata-core.c
drivers/ata/libata-eh.c
include/linux/libata.h

index 2c02208..fcd8586 100644 (file)
@@ -6629,8 +6629,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)
 {
+       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);
 
index 7d1a478..f54b077 100644 (file)
@@ -3423,6 +3423,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:
index be928ca..3d4b5b6 100644 (file)
@@ -182,6 +182,7 @@ enum {
        ATA_LFLAG_DISABLED      = (1 << 6), /* link is disabled */
        ATA_LFLAG_SW_ACTIVITY   = (1 << 7), /* keep activity stats */
        ATA_LFLAG_NO_LPM        = (1 << 8), /* disable LPM on this link */
+       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 */
@@ -284,6 +285,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,
@@ -728,6 +735,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])