summaryrefslogtreecommitdiffstats
path: root/sys/dev/ahci/ahci.c
diff options
context:
space:
mode:
authormav <mav@FreeBSD.org>2011-07-29 20:35:23 +0000
committermav <mav@FreeBSD.org>2011-07-29 20:35:23 +0000
commitb55bc3f9ac95bbcf7a20aaedf5b48b3c137209a4 (patch)
treed32ab9f037cb666812b48e7b171b2b10841a1dd8 /sys/dev/ahci/ahci.c
parent6afa7d74ec658020bda648febc51f45c677cd2a3 (diff)
downloadFreeBSD-src-b55bc3f9ac95bbcf7a20aaedf5b48b3c137209a4.zip
FreeBSD-src-b55bc3f9ac95bbcf7a20aaedf5b48b3c137209a4.tar.gz
In some cases, at least on Marvell 88SE912x controllers, Current Command
Slot field of the PxCMD register may point to an empty command slot. That breaks command timeout detection logic, making impossible to find what command actually caused timeout, and leading to infinite wait. Workaround that by checking whether pointed command slot is really used and can timeout in its time. And if not, fallback to the dumb algorithm used with FBS -- let all commands to time out and then fail all of them. Approved by: re (kib) MFC after: 1 week
Diffstat (limited to 'sys/dev/ahci/ahci.c')
-rw-r--r--sys/dev/ahci/ahci.c20
1 files changed, 14 insertions, 6 deletions
diff --git a/sys/dev/ahci/ahci.c b/sys/dev/ahci/ahci.c
index 0bc094e..1d6185d 100644
--- a/sys/dev/ahci/ahci.c
+++ b/sys/dev/ahci/ahci.c
@@ -1879,12 +1879,13 @@ ahci_execute_transaction(struct ahci_slot *slot)
device_printf(dev, "Poll timeout on slot %d port %d\n",
slot->slot, port);
device_printf(dev, "is %08x cs %08x ss %08x "
- "rs %08x tfd %02x serr %08x\n",
+ "rs %08x tfd %02x serr %08x cmd %08x\n",
ATA_INL(ch->r_mem, AHCI_P_IS),
ATA_INL(ch->r_mem, AHCI_P_CI),
ATA_INL(ch->r_mem, AHCI_P_SACT), ch->rslots,
ATA_INL(ch->r_mem, AHCI_P_TFD),
- ATA_INL(ch->r_mem, AHCI_P_SERR));
+ ATA_INL(ch->r_mem, AHCI_P_SERR),
+ ATA_INL(ch->r_mem, AHCI_P_CMD));
et = AHCI_ERR_TIMEOUT;
}
@@ -1960,8 +1961,12 @@ ahci_timeout(struct ahci_slot *slot)
ccs = (ATA_INL(ch->r_mem, AHCI_P_CMD) & AHCI_P_CMD_CCS_MASK)
>> AHCI_P_CMD_CCS_SHIFT;
if ((sstatus & (1 << slot->slot)) != 0 || ccs == slot->slot ||
- ch->fbs_enabled)
+ ch->fbs_enabled || ch->wrongccs)
slot->state = AHCI_SLOT_EXECUTING;
+ else if ((ch->rslots & (1 << ccs)) == 0) {
+ ch->wrongccs = 1;
+ slot->state = AHCI_SLOT_EXECUTING;
+ }
callout_reset(&slot->timeout,
(int)slot->ccb->ccb_h.timeout * hz / 2000,
@@ -1971,10 +1976,12 @@ ahci_timeout(struct ahci_slot *slot)
device_printf(dev, "Timeout on slot %d port %d\n",
slot->slot, slot->ccb->ccb_h.target_id & 0x0f);
- device_printf(dev, "is %08x cs %08x ss %08x rs %08x tfd %02x serr %08x\n",
+ device_printf(dev, "is %08x cs %08x ss %08x rs %08x tfd %02x "
+ "serr %08x cmd %08x\n",
ATA_INL(ch->r_mem, AHCI_P_IS), ATA_INL(ch->r_mem, AHCI_P_CI),
ATA_INL(ch->r_mem, AHCI_P_SACT), ch->rslots,
- ATA_INL(ch->r_mem, AHCI_P_TFD), ATA_INL(ch->r_mem, AHCI_P_SERR));
+ ATA_INL(ch->r_mem, AHCI_P_TFD), ATA_INL(ch->r_mem, AHCI_P_SERR),
+ ATA_INL(ch->r_mem, AHCI_P_CMD));
/* Handle frozen command. */
if (ch->frozen) {
@@ -1987,7 +1994,7 @@ ahci_timeout(struct ahci_slot *slot)
}
xpt_done(fccb);
}
- if (!ch->fbs_enabled) {
+ if (!ch->fbs_enabled && !ch->wrongccs) {
/* Without FBS we know real timeout source. */
ch->fatalerr = 1;
/* Handle command with timeout. */
@@ -2585,6 +2592,7 @@ ahci_reset(device_t dev)
xpt_release_simq(ch->sim, TRUE);
ch->eslots = 0;
ch->toslots = 0;
+ ch->wrongccs = 0;
ch->fatalerr = 0;
/* Tell the XPT about the event */
xpt_async(AC_BUS_RESET, ch->path, NULL);
OpenPOWER on IntegriCloud