Commit 19b72321 authored by Tejun Heo's avatar Tejun Heo Committed by Jeff Garzik

libata: fix last_reset timestamp handling

ehc->last_reset is used to ensure that resets are not issued too
close to each other.  It's initialized to jiffies minus one minute
on EH entry.  However, when new links are initialized after PMP is
probed, new links have zero for this timestamp resulting in long wait
depending on the current jiffies.

This patch makes last_set considered iff ATA_EHI_DID_RESET is set, in
which case last_reset is always initialized.  As an added precaution,
WARN_ON() is added so that warning is printed if last_reset is
in future.

This problem is spotted and debugged by Shane Huang.
Signed-off-by: default avatarTejun Heo <tj@kernel.org>
Cc: Shane Huang <Shane.Huang@amd.com>
Signed-off-by: default avatarJeff Garzik <jgarzik@redhat.com>
parent 44901a96
...@@ -610,9 +610,6 @@ void ata_scsi_error(struct Scsi_Host *host) ...@@ -610,9 +610,6 @@ void ata_scsi_error(struct Scsi_Host *host)
if (ata_ncq_enabled(dev)) if (ata_ncq_enabled(dev))
ehc->saved_ncq_enabled |= 1 << devno; ehc->saved_ncq_enabled |= 1 << devno;
} }
/* set last reset timestamp to some time in the past */
ehc->last_reset = jiffies - 60 * HZ;
} }
ap->pflags |= ATA_PFLAG_EH_IN_PROGRESS; ap->pflags |= ATA_PFLAG_EH_IN_PROGRESS;
...@@ -2281,17 +2278,21 @@ int ata_eh_reset(struct ata_link *link, int classify, ...@@ -2281,17 +2278,21 @@ int ata_eh_reset(struct ata_link *link, int classify,
if (link->flags & ATA_LFLAG_NO_SRST) if (link->flags & ATA_LFLAG_NO_SRST)
softreset = NULL; softreset = NULL;
/* make sure each reset attemp is at least COOL_DOWN apart */
if (ehc->i.flags & ATA_EHI_DID_RESET) {
now = jiffies; now = jiffies;
deadline = ata_deadline(ehc->last_reset, ATA_EH_RESET_COOL_DOWN); WARN_ON(time_after(ehc->last_reset, now));
deadline = ata_deadline(ehc->last_reset,
ATA_EH_RESET_COOL_DOWN);
if (time_before(now, deadline)) if (time_before(now, deadline))
schedule_timeout_uninterruptible(deadline - now); schedule_timeout_uninterruptible(deadline - now);
}
spin_lock_irqsave(ap->lock, flags); spin_lock_irqsave(ap->lock, flags);
ap->pflags |= ATA_PFLAG_RESETTING; ap->pflags |= ATA_PFLAG_RESETTING;
spin_unlock_irqrestore(ap->lock, flags); spin_unlock_irqrestore(ap->lock, flags);
ata_eh_about_to_do(link, NULL, ATA_EH_RESET); ata_eh_about_to_do(link, NULL, ATA_EH_RESET);
ehc->last_reset = jiffies;
ata_link_for_each_dev(dev, link) { ata_link_for_each_dev(dev, link) {
/* If we issue an SRST then an ATA drive (not ATAPI) /* If we issue an SRST then an ATA drive (not ATAPI)
...@@ -2379,7 +2380,6 @@ int ata_eh_reset(struct ata_link *link, int classify, ...@@ -2379,7 +2380,6 @@ int ata_eh_reset(struct ata_link *link, int classify,
/* /*
* Perform reset * Perform reset
*/ */
ehc->last_reset = jiffies;
if (ata_is_host_link(link)) if (ata_is_host_link(link))
ata_eh_freeze_port(ap); ata_eh_freeze_port(ap);
...@@ -2391,6 +2391,7 @@ int ata_eh_reset(struct ata_link *link, int classify, ...@@ -2391,6 +2391,7 @@ int ata_eh_reset(struct ata_link *link, int classify,
reset == softreset ? "soft" : "hard"); reset == softreset ? "soft" : "hard");
/* mark that this EH session started with reset */ /* mark that this EH session started with reset */
ehc->last_reset = jiffies;
if (reset == hardreset) if (reset == hardreset)
ehc->i.flags |= ATA_EHI_DID_HARDRESET; ehc->i.flags |= ATA_EHI_DID_HARDRESET;
else else
...@@ -2535,7 +2536,7 @@ int ata_eh_reset(struct ata_link *link, int classify, ...@@ -2535,7 +2536,7 @@ int ata_eh_reset(struct ata_link *link, int classify,
ata_eh_done(link, NULL, ATA_EH_RESET); ata_eh_done(link, NULL, ATA_EH_RESET);
if (slave) if (slave)
ata_eh_done(slave, NULL, ATA_EH_RESET); ata_eh_done(slave, NULL, ATA_EH_RESET);
ehc->last_reset = jiffies; ehc->last_reset = jiffies; /* update to completion time */
ehc->i.action |= ATA_EH_REVALIDATE; ehc->i.action |= ATA_EH_REVALIDATE;
rc = 0; rc = 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