- Rework timeout handling, to make it more graceful for devices sharing
controller port (with PMP). Wait for other commands completion/timeout
before initiating recovery.
- Handle timeouts and fatal errors with port hard-reset. The rest of
recovery will be done by XPT on receiving async event. More gracefull
per-device soft-reset recovery can be implemented later.
This commit is contained in:
Alexander Motin 2009-11-03 12:03:13 +00:00
parent a69552e4b6
commit 6f9a51c735
2 changed files with 104 additions and 49 deletions

View File

@ -511,7 +511,10 @@ siis_ch_resume(device_t dev)
/* Get port out of reset state. */ /* Get port out of reset state. */
ATA_OUTL(ch->r_mem, SIIS_P_CTLCLR, SIIS_P_CTL_PORT_RESET); ATA_OUTL(ch->r_mem, SIIS_P_CTLCLR, SIIS_P_CTL_PORT_RESET);
ATA_OUTL(ch->r_mem, SIIS_P_CTLCLR, SIIS_P_CTL_32BIT); ATA_OUTL(ch->r_mem, SIIS_P_CTLCLR, SIIS_P_CTL_32BIT);
ATA_OUTL(ch->r_mem, SIIS_P_CTLCLR, SIIS_P_CTL_PME); if (ch->pm_present)
ATA_OUTL(ch->r_mem, SIIS_P_CTLSET, SIIS_P_CTL_PME);
else
ATA_OUTL(ch->r_mem, SIIS_P_CTLCLR, SIIS_P_CTL_PME);
/* Enable port interrupts */ /* Enable port interrupts */
ATA_OUTL(ch->r_mem, SIIS_P_IESET, SIIS_P_IX_ENABLED); ATA_OUTL(ch->r_mem, SIIS_P_IESET, SIIS_P_IX_ENABLED);
return (0); return (0);
@ -764,7 +767,7 @@ siis_ch_intr(void *data)
estatus == SIIS_P_CMDERR_DATAFIS) { estatus == SIIS_P_CMDERR_DATAFIS) {
tslots = ch->numtslots[port]; tslots = ch->numtslots[port];
for (i = 0; i < SIIS_MAX_SLOTS; i++) { for (i = 0; i < SIIS_MAX_SLOTS; i++) {
/* XXX: reqests in loading state. */ /* XXX: requests in loading state. */
if (((ch->rslots >> i) & 1) == 0) if (((ch->rslots >> i) & 1) == 0)
continue; continue;
if (ch->slot[i].ccb->ccb_h.target_id != port) if (ch->slot[i].ccb->ccb_h.target_id != port)
@ -796,7 +799,7 @@ siis_ch_intr(void *data)
} else } else
et = SIIS_ERR_INVALID; et = SIIS_ERR_INVALID;
for (i = 0; i < SIIS_MAX_SLOTS; i++) { for (i = 0; i < SIIS_MAX_SLOTS; i++) {
/* XXX: reqests in loading state. */ /* XXX: requests in loading state. */
if (((ch->rslots >> i) & 1) == 0) if (((ch->rslots >> i) & 1) == 0)
continue; continue;
siis_end_transaction(&ch->slot[i], et); siis_end_transaction(&ch->slot[i], et);
@ -967,13 +970,33 @@ siis_execute_transaction(struct siis_slot *slot)
return; return;
} }
/* Must be called with channel locked. */
static void
siis_process_timeout(device_t dev)
{
struct siis_channel *ch = device_get_softc(dev);
int i;
mtx_assert(&ch->mtx, MA_OWNED);
if (!ch->readlog && !ch->recovery) {
xpt_freeze_simq(ch->sim, ch->numrslots);
ch->recovery = 1;
}
/* Handle the rest of commands. */
for (i = 0; i < SIIS_MAX_SLOTS; i++) {
/* Do we have a running request on slot? */
if (ch->slot[i].state < SIIS_SLOT_RUNNING)
continue;
siis_end_transaction(&ch->slot[i], SIIS_ERR_TIMEOUT);
}
}
/* Locked by callout mechanism. */ /* Locked by callout mechanism. */
static void static void
siis_timeout(struct siis_slot *slot) siis_timeout(struct siis_slot *slot)
{ {
device_t dev = slot->dev; device_t dev = slot->dev;
struct siis_channel *ch = device_get_softc(dev); struct siis_channel *ch = device_get_softc(dev);
int i;
mtx_assert(&ch->mtx, MA_OWNED); mtx_assert(&ch->mtx, MA_OWNED);
device_printf(dev, "Timeout on slot %d\n", slot->slot); device_printf(dev, "Timeout on slot %d\n", slot->slot);
@ -981,32 +1004,15 @@ device_printf(dev, "%s is %08x ss %08x rs %08x es %08x sts %08x serr %08x\n",
__func__, ATA_INL(ch->r_mem, SIIS_P_IS), ATA_INL(ch->r_mem, SIIS_P_SS), ch->rslots, __func__, ATA_INL(ch->r_mem, SIIS_P_IS), ATA_INL(ch->r_mem, SIIS_P_SS), ch->rslots,
ATA_INL(ch->r_mem, SIIS_P_CMDERR), ATA_INL(ch->r_mem, SIIS_P_STS), ATA_INL(ch->r_mem, SIIS_P_CMDERR), ATA_INL(ch->r_mem, SIIS_P_STS),
ATA_INL(ch->r_mem, SIIS_P_SERR)); ATA_INL(ch->r_mem, SIIS_P_SERR));
/* Kick controller into sane state. */
siis_portinit(ch->dev);
if (!ch->readlog) if (ch->toslots == 0)
xpt_freeze_simq(ch->sim, ch->numrslots); xpt_freeze_simq(ch->sim, 1);
/* Handle frozen command. */ ch->toslots |= (1 << slot->slot);
if (ch->frozen) { if ((ch->rslots & ~ch->toslots) == 0)
union ccb *fccb = ch->frozen; siis_process_timeout(dev);
ch->frozen = NULL; else
fccb->ccb_h.status &= ~CAM_STATUS_MASK; device_printf(dev, " ... waiting for slots %08x\n",
fccb->ccb_h.status |= CAM_REQUEUE_REQ | CAM_RELEASE_SIMQ; ch->rslots & ~ch->toslots);
if (!(fccb->ccb_h.status & CAM_DEV_QFRZN)) {
xpt_freeze_devq(fccb->ccb_h.path, 1);
fccb->ccb_h.status |= CAM_DEV_QFRZN;
}
xpt_done(fccb);
}
/* Handle command with timeout. */
siis_end_transaction(&ch->slot[slot->slot], SIIS_ERR_TIMEOUT);
/* Handle the rest of commands. */
for (i = 0; i < SIIS_MAX_SLOTS; i++) {
/* Do we have a running request on slot? */
if (ch->slot[i].state < SIIS_SLOT_RUNNING)
continue;
siis_end_transaction(&ch->slot[i], SIIS_ERR_INNOCENT);
}
} }
/* Must be called with channel locked. */ /* Must be called with channel locked. */
@ -1071,6 +1077,7 @@ siis_end_transaction(struct siis_slot *slot, enum siis_err_type et)
ccb->csio.scsi_status = SCSI_STATUS_OK; ccb->csio.scsi_status = SCSI_STATUS_OK;
break; break;
case SIIS_ERR_INVALID: case SIIS_ERR_INVALID:
ch->fatalerr = 1;
ccb->ccb_h.status |= CAM_REQ_INVALID; ccb->ccb_h.status |= CAM_REQ_INVALID;
break; break;
case SIIS_ERR_INNOCENT: case SIIS_ERR_INNOCENT:
@ -1086,9 +1093,11 @@ siis_end_transaction(struct siis_slot *slot, enum siis_err_type et)
} }
break; break;
case SIIS_ERR_SATA: case SIIS_ERR_SATA:
ch->fatalerr = 1;
ccb->ccb_h.status |= CAM_UNCOR_PARITY; ccb->ccb_h.status |= CAM_UNCOR_PARITY;
break; break;
case SIIS_ERR_TIMEOUT: case SIIS_ERR_TIMEOUT:
ch->fatalerr = 1;
ccb->ccb_h.status |= CAM_CMD_TIMEOUT; ccb->ccb_h.status |= CAM_CMD_TIMEOUT;
break; break;
default: default:
@ -1097,6 +1106,11 @@ siis_end_transaction(struct siis_slot *slot, enum siis_err_type et)
/* Free slot. */ /* Free slot. */
ch->rslots &= ~(1 << slot->slot); ch->rslots &= ~(1 << slot->slot);
ch->aslots &= ~(1 << slot->slot); ch->aslots &= ~(1 << slot->slot);
if (et != SIIS_ERR_TIMEOUT) {
if (ch->toslots == (1 << slot->slot))
xpt_release_simq(ch->sim, TRUE);
ch->toslots &= ~(1 << slot->slot);
}
slot->state = SIIS_SLOT_EMPTY; slot->state = SIIS_SLOT_EMPTY;
slot->ccb = NULL; slot->ccb = NULL;
/* Update channel stats. */ /* Update channel stats. */
@ -1105,13 +1119,14 @@ siis_end_transaction(struct siis_slot *slot, enum siis_err_type et)
(ccb->ataio.cmd.flags & CAM_ATAIO_FPDMA)) { (ccb->ataio.cmd.flags & CAM_ATAIO_FPDMA)) {
ch->numtslots[ccb->ccb_h.target_id]--; ch->numtslots[ccb->ccb_h.target_id]--;
} }
/* If it was our READ LOG command - process it. */
if (ch->readlog) {
siis_process_read_log(dev, ccb);
/* If it was NCQ command error, put result on hold. */ /* If it was NCQ command error, put result on hold. */
if (et == SIIS_ERR_NCQ) { } else if (et == SIIS_ERR_NCQ) {
ch->hold[slot->slot] = ccb; ch->hold[slot->slot] = ccb;
ch->numhslots++; ch->numhslots++;
} else if (ch->readlog) /* If it was our READ LOG command - process it. */ } else
siis_process_read_log(dev, ccb);
else
xpt_done(ccb); xpt_done(ccb);
/* Unfreeze frozen command. */ /* Unfreeze frozen command. */
if (ch->frozen && ch->numrslots == 0) { if (ch->frozen && ch->numrslots == 0) {
@ -1122,13 +1137,20 @@ siis_end_transaction(struct siis_slot *slot, enum siis_err_type et)
} }
/* If we have no other active commands, ... */ /* If we have no other active commands, ... */
if (ch->rslots == 0) { if (ch->rslots == 0) {
/* if we have slots in error, we can reinit port. */ /* if there were timeouts or fatal error - reset port. */
if (ch->eslots != 0) if (ch->toslots != 0 || ch->fatalerr) {
siis_portinit(dev); siis_reset(dev);
/* if there commands on hold, we can do READ LOG. */ } else {
if (!ch->readlog && ch->numhslots) /* if we have slots in error, we can reinit port. */
siis_issue_read_log(dev); if (ch->eslots != 0)
} siis_portinit(dev);
/* if there commands on hold, we can do READ LOG. */
if (!ch->readlog && ch->numhslots)
siis_issue_read_log(dev);
}
/* If all the reset of commands are in timeout - abort them. */
} else if ((ch->rslots & ~ch->toslots) == 0)
siis_process_timeout(dev);
} }
static void static void
@ -1296,13 +1318,14 @@ static void
siis_reset(device_t dev) siis_reset(device_t dev)
{ {
struct siis_channel *ch = device_get_softc(dev); struct siis_channel *ch = device_get_softc(dev);
int i; int i, retry = 0;
uint32_t val; uint32_t val;
if (bootverbose) if (bootverbose)
device_printf(dev, "SIIS reset...\n"); device_printf(dev, "SIIS reset...\n");
xpt_freeze_simq(ch->sim, ch->numrslots); if (!ch->readlog && !ch->recovery)
/* Requeue freezed command. */ xpt_freeze_simq(ch->sim, ch->numrslots);
/* Requeue frozen command. */
if (ch->frozen) { if (ch->frozen) {
union ccb *fccb = ch->frozen; union ccb *fccb = ch->frozen;
ch->frozen = NULL; ch->frozen = NULL;
@ -1322,6 +1345,20 @@ siis_reset(device_t dev)
/* XXX; Commands in loading state. */ /* XXX; Commands in loading state. */
siis_end_transaction(&ch->slot[i], SIIS_ERR_INNOCENT); siis_end_transaction(&ch->slot[i], SIIS_ERR_INNOCENT);
} }
/* Finish all holden commands as-is. */
for (i = 0; i < SIIS_MAX_SLOTS; i++) {
if (!ch->hold[i])
continue;
xpt_done(ch->hold[i]);
ch->hold[i] = NULL;
ch->numhslots--;
}
if (ch->toslots != 0)
xpt_release_simq(ch->sim, TRUE);
ch->eslots = 0;
ch->recovery = 0;
ch->toslots = 0;
ch->fatalerr = 0;
/* Disable port interrupts */ /* Disable port interrupts */
ATA_OUTL(ch->r_mem, SIIS_P_IECLR, 0x0000FFFF); ATA_OUTL(ch->r_mem, SIIS_P_IECLR, 0x0000FFFF);
/* Set speed limit. */ /* Set speed limit. */
@ -1336,6 +1373,7 @@ siis_reset(device_t dev)
ATA_OUTL(ch->r_mem, SIIS_P_SCTL, ATA_OUTL(ch->r_mem, SIIS_P_SCTL,
ATA_SC_DET_IDLE | val | ((ch->pm_level > 0) ? 0 : ATA_SC_DET_IDLE | val | ((ch->pm_level > 0) ? 0 :
(ATA_SC_IPM_DIS_PARTIAL | ATA_SC_IPM_DIS_SLUMBER))); (ATA_SC_IPM_DIS_PARTIAL | ATA_SC_IPM_DIS_SLUMBER)));
retry:
siis_devreset(dev); siis_devreset(dev);
/* Reset and reconnect PHY, */ /* Reset and reconnect PHY, */
if (!siis_sata_connect(ch)) { if (!siis_sata_connect(ch)) {
@ -1350,8 +1388,25 @@ siis_reset(device_t dev)
return; return;
} }
/* Wait for clearing busy status. */ /* Wait for clearing busy status. */
if (siis_wait_ready(dev, 10000)) if (siis_wait_ready(dev, 10000)) {
device_printf(dev, "device ready timeout\n"); device_printf(dev, "device ready timeout\n");
if (!retry) {
device_printf(dev, "trying full port reset ...\n");
/* Get port to the reset state. */
ATA_OUTL(ch->r_mem, SIIS_P_CTLSET, SIIS_P_CTL_PORT_RESET);
DELAY(10000);
/* Get port out of reset state. */
ATA_OUTL(ch->r_mem, SIIS_P_CTLCLR, SIIS_P_CTL_PORT_RESET);
ATA_OUTL(ch->r_mem, SIIS_P_CTLCLR, SIIS_P_CTL_32BIT);
if (ch->pm_present)
ATA_OUTL(ch->r_mem, SIIS_P_CTLSET, SIIS_P_CTL_PME);
else
ATA_OUTL(ch->r_mem, SIIS_P_CTLCLR, SIIS_P_CTL_PME);
siis_wait_ready(dev, 5000);
retry = 1;
goto retry;
}
}
ch->devices = 1; ch->devices = 1;
/* Enable port interrupts */ /* Enable port interrupts */
ATA_OUTL(ch->r_mem, SIIS_P_IS, 0xFFFFFFFF); ATA_OUTL(ch->r_mem, SIIS_P_IS, 0xFFFFFFFF);
@ -1487,7 +1542,8 @@ siisaction(struct cam_sim *sim, union ccb *ccb)
struct ccb_trans_settings *cts = &ccb->cts; struct ccb_trans_settings *cts = &ccb->cts;
if (cts->xport_specific.sata.valid & CTS_SATA_VALID_PM) { if (cts->xport_specific.sata.valid & CTS_SATA_VALID_PM) {
if (cts->xport_specific.sata.pm_present) ch->pm_present = cts->xport_specific.sata.pm_present;
if (ch->pm_present)
ATA_OUTL(ch->r_mem, SIIS_P_CTLSET, SIIS_P_CTL_PME); ATA_OUTL(ch->r_mem, SIIS_P_CTLSET, SIIS_P_CTL_PME);
else else
ATA_OUTL(ch->r_mem, SIIS_P_CTLCLR, SIIS_P_CTL_PME); ATA_OUTL(ch->r_mem, SIIS_P_CTLCLR, SIIS_P_CTL_PME);
@ -1522,9 +1578,7 @@ siisaction(struct cam_sim *sim, union ccb *ccb)
cts->xport_specific.sata.bitrate = 150000; cts->xport_specific.sata.bitrate = 150000;
cts->xport_specific.sata.valid |= CTS_SATA_VALID_SPEED; cts->xport_specific.sata.valid |= CTS_SATA_VALID_SPEED;
} }
cts->xport_specific.sata.pm_present = cts->xport_specific.sata.pm_present = ch->pm_present;
(ATA_INL(ch->r_mem, SIIS_P_STS) & SIIS_P_CTL_PME) ?
1 : 0;
cts->xport_specific.sata.valid |= CTS_SATA_VALID_PM; cts->xport_specific.sata.valid |= CTS_SATA_VALID_PM;
ccb->ccb_h.status = CAM_REQ_CMP; ccb->ccb_h.status = CAM_REQ_CMP;
xpt_done(ccb); xpt_done(ccb);

View File

@ -373,13 +373,14 @@ struct siis_channel {
uint32_t rslots; /* Running slots */ uint32_t rslots; /* Running slots */
uint32_t aslots; /* Slots with atomic commands */ uint32_t aslots; /* Slots with atomic commands */
uint32_t eslots; /* Slots in error */ uint32_t eslots; /* Slots in error */
uint32_t toslots; /* Slots in timeout */
int numrslots; /* Number of running slots */ int numrslots; /* Number of running slots */
int numtslots[SIIS_MAX_SLOTS]; /* Number of tagged slots */ int numtslots[SIIS_MAX_SLOTS]; /* Number of tagged slots */
int numhslots; /* Number of holden slots */ int numhslots; /* Number of holden slots */
int readlog; /* Our READ LOG active */ int readlog; /* Our READ LOG active */
int fatalerr; /* Fatal error happend */
int recovery; /* Some slots are in error */ int recovery; /* Some slots are in error */
int lastslot; /* Last used slot */ int lastslot; /* Last used slot */
int taggedtarget; /* Last tagged target */
union ccb *frozen; /* Frozen command */ union ccb *frozen; /* Frozen command */
}; };