MFp4:
- 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:
parent
a69552e4b6
commit
6f9a51c735
@ -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);
|
||||||
|
@ -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 */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user