|
@@ -82,6 +82,10 @@ enum {
|
|
|
ATA_EH_FASTDRAIN_INTERVAL = 3000,
|
|
|
|
|
|
ATA_EH_UA_TRIES = 5,
|
|
|
+
|
|
|
+ /* probe speed down parameters, see ata_eh_schedule_probe() */
|
|
|
+ ATA_EH_PROBE_TRIAL_INTERVAL = 60000, /* 1 min */
|
|
|
+ ATA_EH_PROBE_TRIALS = 2,
|
|
|
};
|
|
|
|
|
|
/* The following table determines how we sequence resets. Each entry
|
|
@@ -2975,9 +2979,24 @@ static int ata_eh_skip_recovery(struct ata_link *link)
|
|
|
return 1;
|
|
|
}
|
|
|
|
|
|
+static int ata_count_probe_trials_cb(struct ata_ering_entry *ent, void *void_arg)
|
|
|
+{
|
|
|
+ u64 interval = msecs_to_jiffies(ATA_EH_PROBE_TRIAL_INTERVAL);
|
|
|
+ u64 now = get_jiffies_64();
|
|
|
+ int *trials = void_arg;
|
|
|
+
|
|
|
+ if (ent->timestamp < now - min(now, interval))
|
|
|
+ return -1;
|
|
|
+
|
|
|
+ (*trials)++;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
static int ata_eh_schedule_probe(struct ata_device *dev)
|
|
|
{
|
|
|
struct ata_eh_context *ehc = &dev->link->eh_context;
|
|
|
+ struct ata_link *link = ata_dev_phys_link(dev);
|
|
|
+ int trials = 0;
|
|
|
|
|
|
if (!(ehc->i.probe_mask & (1 << dev->devno)) ||
|
|
|
(ehc->did_probe_mask & (1 << dev->devno)))
|
|
@@ -2990,6 +3009,25 @@ static int ata_eh_schedule_probe(struct ata_device *dev)
|
|
|
ehc->saved_xfer_mode[dev->devno] = 0;
|
|
|
ehc->saved_ncq_enabled &= ~(1 << dev->devno);
|
|
|
|
|
|
+ /* Record and count probe trials on the ering. The specific
|
|
|
+ * error mask used is irrelevant. Because a successful device
|
|
|
+ * detection clears the ering, this count accumulates only if
|
|
|
+ * there are consecutive failed probes.
|
|
|
+ *
|
|
|
+ * If the count is equal to or higher than ATA_EH_PROBE_TRIALS
|
|
|
+ * in the last ATA_EH_PROBE_TRIAL_INTERVAL, link speed is
|
|
|
+ * forced to 1.5Gbps.
|
|
|
+ *
|
|
|
+ * This is to work around cases where failed link speed
|
|
|
+ * negotiation results in device misdetection leading to
|
|
|
+ * infinite DEVXCHG or PHRDY CHG events.
|
|
|
+ */
|
|
|
+ ata_ering_record(&dev->ering, 0, AC_ERR_OTHER);
|
|
|
+ ata_ering_map(&dev->ering, ata_count_probe_trials_cb, &trials);
|
|
|
+
|
|
|
+ if (trials > ATA_EH_PROBE_TRIALS)
|
|
|
+ sata_down_spd_limit(link, 1);
|
|
|
+
|
|
|
return 1;
|
|
|
}
|
|
|
|