Commit 19b723218bde79c60a394a3caee9eb156ac2d356

Authored by Tejun Heo
Committed by Jeff Garzik
1 parent 44901a9684

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: Tejun Heo <tj@kernel.org>
Cc: Shane Huang <Shane.Huang@amd.com>
Signed-off-by: Jeff Garzik <jgarzik@redhat.com>

Showing 1 changed file with 11 additions and 10 deletions Side-by-side Diff

drivers/ata/libata-eh.c
... ... @@ -610,9 +610,6 @@
610 610 if (ata_ncq_enabled(dev))
611 611 ehc->saved_ncq_enabled |= 1 << devno;
612 612 }
613   -
614   - /* set last reset timestamp to some time in the past */
615   - ehc->last_reset = jiffies - 60 * HZ;
616 613 }
617 614  
618 615 ap->pflags |= ATA_PFLAG_EH_IN_PROGRESS;
619 616  
... ... @@ -2281,17 +2278,21 @@
2281 2278 if (link->flags & ATA_LFLAG_NO_SRST)
2282 2279 softreset = NULL;
2283 2280  
2284   - now = jiffies;
2285   - deadline = ata_deadline(ehc->last_reset, ATA_EH_RESET_COOL_DOWN);
2286   - if (time_before(now, deadline))
2287   - schedule_timeout_uninterruptible(deadline - now);
  2281 + /* make sure each reset attemp is at least COOL_DOWN apart */
  2282 + if (ehc->i.flags & ATA_EHI_DID_RESET) {
  2283 + now = jiffies;
  2284 + WARN_ON(time_after(ehc->last_reset, now));
  2285 + deadline = ata_deadline(ehc->last_reset,
  2286 + ATA_EH_RESET_COOL_DOWN);
  2287 + if (time_before(now, deadline))
  2288 + schedule_timeout_uninterruptible(deadline - now);
  2289 + }
2288 2290  
2289 2291 spin_lock_irqsave(ap->lock, flags);
2290 2292 ap->pflags |= ATA_PFLAG_RESETTING;
2291 2293 spin_unlock_irqrestore(ap->lock, flags);
2292 2294  
2293 2295 ata_eh_about_to_do(link, NULL, ATA_EH_RESET);
2294   - ehc->last_reset = jiffies;
2295 2296  
2296 2297 ata_link_for_each_dev(dev, link) {
2297 2298 /* If we issue an SRST then an ATA drive (not ATAPI)
... ... @@ -2379,7 +2380,6 @@
2379 2380 /*
2380 2381 * Perform reset
2381 2382 */
2382   - ehc->last_reset = jiffies;
2383 2383 if (ata_is_host_link(link))
2384 2384 ata_eh_freeze_port(ap);
2385 2385  
... ... @@ -2391,6 +2391,7 @@
2391 2391 reset == softreset ? "soft" : "hard");
2392 2392  
2393 2393 /* mark that this EH session started with reset */
  2394 + ehc->last_reset = jiffies;
2394 2395 if (reset == hardreset)
2395 2396 ehc->i.flags |= ATA_EHI_DID_HARDRESET;
2396 2397 else
... ... @@ -2535,7 +2536,7 @@
2535 2536 ata_eh_done(link, NULL, ATA_EH_RESET);
2536 2537 if (slave)
2537 2538 ata_eh_done(slave, NULL, ATA_EH_RESET);
2538   - ehc->last_reset = jiffies;
  2539 + ehc->last_reset = jiffies; /* update to completion time */
2539 2540 ehc->i.action |= ATA_EH_REVALIDATE;
2540 2541  
2541 2542 rc = 0;