Implement automatic SCSI sense fetching for ahci(4).

This commit is contained in:
mav 2011-04-12 11:24:59 +00:00
parent f70691f628
commit 983e9f3338
2 changed files with 92 additions and 33 deletions

View File

@ -91,8 +91,9 @@ static int ahci_sata_connect(struct ahci_channel *ch);
static int ahci_sata_phy_reset(device_t dev);
static int ahci_wait_ready(device_t dev, int t);
static void ahci_issue_read_log(device_t dev);
static void ahci_issue_recovery(device_t dev);
static void ahci_process_read_log(device_t dev, union ccb *ccb);
static void ahci_process_request_sense(device_t dev, union ccb *ccb);
static void ahciaction(struct cam_sim *sim, union ccb *ccb);
static void ahcipoll(struct cam_sim *sim);
@ -267,6 +268,12 @@ static struct {
{0x00000000, 0x00, NULL, 0}
};
#define recovery_type spriv_field0
#define RECOVERY_NONE 0
#define RECOVERY_READ_LOG 1
#define RECOVERY_REQUEST_SENSE 2
#define recovery_slot spriv_field1
static int
ahci_probe(device_t dev)
{
@ -1465,7 +1472,7 @@ ahci_ch_intr(void *data)
* We can't reinit port if there are some other
* commands active, use resume to complete them.
*/
if (ch->rslots != 0)
if (ch->rslots != 0 && !ch->recoverycmd)
ATA_OUTL(ch->r_mem, AHCI_P_FBS, AHCI_P_FBS_EN | AHCI_P_FBS_DEC);
}
/* Process NOTIFY events */
@ -1937,7 +1944,7 @@ ahci_end_transaction(struct ahci_slot *slot, enum ahci_err_type et)
if (et != AHCI_ERR_NONE)
ch->eslots |= (1 << slot->slot);
/* In case of error, freeze device for proper recovery. */
if ((et != AHCI_ERR_NONE) && (!ch->readlog) &&
if ((et != AHCI_ERR_NONE) && (!ch->recoverycmd) &&
!(ccb->ccb_h.status & CAM_DEV_QFRZN)) {
xpt_freeze_devq(ccb->ccb_h.path, 1);
ccb->ccb_h.status |= CAM_DEV_QFRZN;
@ -1968,7 +1975,7 @@ ahci_end_transaction(struct ahci_slot *slot, enum ahci_err_type et)
break;
case AHCI_ERR_SATA:
ch->fatalerr = 1;
if (!ch->readlog) {
if (!ch->recoverycmd) {
xpt_freeze_simq(ch->sim, 1);
ccb->ccb_h.status &= ~CAM_STATUS_MASK;
ccb->ccb_h.status |= CAM_RELEASE_SIMQ;
@ -1976,7 +1983,7 @@ ahci_end_transaction(struct ahci_slot *slot, enum ahci_err_type et)
ccb->ccb_h.status |= CAM_UNCOR_PARITY;
break;
case AHCI_ERR_TIMEOUT:
if (!ch->readlog) {
if (!ch->recoverycmd) {
xpt_freeze_simq(ch->sim, 1);
ccb->ccb_h.status &= ~CAM_STATUS_MASK;
ccb->ccb_h.status |= CAM_RELEASE_SIMQ;
@ -2019,10 +2026,15 @@ ahci_end_transaction(struct ahci_slot *slot, enum ahci_err_type et)
return;
}
/* If it was our READ LOG command - process it. */
if (ch->readlog) {
if (ccb->ccb_h.recovery_type == RECOVERY_READ_LOG) {
ahci_process_read_log(dev, ccb);
/* If it was NCQ command error, put result on hold. */
} else if (et == AHCI_ERR_NCQ) {
/* If it was our REQUEST SENSE command - process it. */
} else if (ccb->ccb_h.recovery_type == RECOVERY_REQUEST_SENSE) {
ahci_process_request_sense(dev, ccb);
/* If it was NCQ or ATAPI command error, put result on hold. */
} else if (et == AHCI_ERR_NCQ ||
((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_SCSI_STATUS_ERROR &&
(ccb->ccb_h.flags & CAM_DIS_AUTOSENSE) == 0)) {
ch->hold[slot->slot] = ccb;
ch->numhslots++;
} else
@ -2046,8 +2058,8 @@ ahci_end_transaction(struct ahci_slot *slot, enum ahci_err_type et)
ahci_start(dev, 1);
}
/* if there commands on hold, we can do READ LOG. */
if (!ch->readlog && ch->numhslots)
ahci_issue_read_log(dev);
if (!ch->recoverycmd && ch->numhslots)
ahci_issue_recovery(dev);
}
/* If all the rest of commands are in timeout - give them chance. */
} else if ((ch->rslots & ~ch->toslots) == 0 &&
@ -2062,14 +2074,15 @@ ahci_end_transaction(struct ahci_slot *slot, enum ahci_err_type et)
}
static void
ahci_issue_read_log(device_t dev)
ahci_issue_recovery(device_t dev)
{
struct ahci_channel *ch = device_get_softc(dev);
union ccb *ccb;
struct ccb_ataio *ataio;
struct ccb_scsiio *csio;
int i;
ch->readlog = 1;
ch->recoverycmd = 1;
/* Find some holden command. */
for (i = 0; i < ch->numslots; i++) {
if (ch->hold[i])
@ -2081,26 +2094,45 @@ ahci_issue_read_log(device_t dev)
return; /* XXX */
}
ccb->ccb_h = ch->hold[i]->ccb_h; /* Reuse old header. */
ccb->ccb_h.func_code = XPT_ATA_IO;
ccb->ccb_h.flags = CAM_DIR_IN;
ccb->ccb_h.timeout = 1000; /* 1s should be enough. */
ataio = &ccb->ataio;
ataio->data_ptr = malloc(512, M_AHCI, M_NOWAIT);
if (ataio->data_ptr == NULL) {
xpt_free_ccb(ccb);
device_printf(dev, "Unable allocate memory for READ LOG command");
return; /* XXX */
if (ccb->ccb_h.func_code == XPT_ATA_IO) {
/* READ LOG */
ccb->ccb_h.recovery_type = RECOVERY_READ_LOG;
ccb->ccb_h.func_code = XPT_ATA_IO;
ccb->ccb_h.flags = CAM_DIR_IN;
ccb->ccb_h.timeout = 1000; /* 1s should be enough. */
ataio = &ccb->ataio;
ataio->data_ptr = malloc(512, M_AHCI, M_NOWAIT);
if (ataio->data_ptr == NULL) {
xpt_free_ccb(ccb);
device_printf(dev, "Unable allocate memory for READ LOG command");
return; /* XXX */
}
ataio->dxfer_len = 512;
bzero(&ataio->cmd, sizeof(ataio->cmd));
ataio->cmd.flags = CAM_ATAIO_48BIT;
ataio->cmd.command = 0x2F; /* READ LOG EXT */
ataio->cmd.sector_count = 1;
ataio->cmd.sector_count_exp = 0;
ataio->cmd.lba_low = 0x10;
ataio->cmd.lba_mid = 0;
ataio->cmd.lba_mid_exp = 0;
} else {
/* REQUEST SENSE */
ccb->ccb_h.recovery_type = RECOVERY_REQUEST_SENSE;
ccb->ccb_h.recovery_slot = i;
ccb->ccb_h.func_code = XPT_SCSI_IO;
ccb->ccb_h.flags = CAM_DIR_IN;
ccb->ccb_h.status = 0;
ccb->ccb_h.timeout = 1000; /* 1s should be enough. */
csio = &ccb->csio;
csio->data_ptr = (void *)&ch->hold[i]->csio.sense_data;
csio->dxfer_len = ch->hold[i]->csio.sense_len;
csio->cdb_len = 6;
bzero(&csio->cdb_io, sizeof(csio->cdb_io));
csio->cdb_io.cdb_bytes[0] = 0x03;
csio->cdb_io.cdb_bytes[4] = csio->dxfer_len;
}
ataio->dxfer_len = 512;
bzero(&ataio->cmd, sizeof(ataio->cmd));
ataio->cmd.flags = CAM_ATAIO_48BIT;
ataio->cmd.command = 0x2F; /* READ LOG EXT */
ataio->cmd.sector_count = 1;
ataio->cmd.sector_count_exp = 0;
ataio->cmd.lba_low = 0x10;
ataio->cmd.lba_mid = 0;
ataio->cmd.lba_mid_exp = 0;
/* Freeze SIM while doing READ LOG EXT. */
/* Freeze SIM while doing recovery. */
xpt_freeze_simq(ch->sim, 1);
ahci_begin_transaction(dev, ccb);
}
@ -2113,7 +2145,7 @@ ahci_process_read_log(device_t dev, union ccb *ccb)
struct ata_res *res;
int i;
ch->readlog = 0;
ch->recoverycmd = 0;
data = ccb->ataio.data_ptr;
if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP &&
@ -2121,6 +2153,8 @@ ahci_process_read_log(device_t dev, union ccb *ccb)
for (i = 0; i < ch->numslots; i++) {
if (!ch->hold[i])
continue;
if (ch->hold[i]->ccb_h.func_code != XPT_ATA_IO)
continue;
if ((data[0] & 0x1F) == i) {
res = &ch->hold[i]->ataio.res;
res->status = data[2];
@ -2151,6 +2185,8 @@ ahci_process_read_log(device_t dev, union ccb *ccb)
for (i = 0; i < ch->numslots; i++) {
if (!ch->hold[i])
continue;
if (ch->hold[i]->ccb_h.func_code != XPT_ATA_IO)
continue;
xpt_done(ch->hold[i]);
ch->hold[i] = NULL;
ch->numhslots--;
@ -2161,6 +2197,28 @@ ahci_process_read_log(device_t dev, union ccb *ccb)
xpt_release_simq(ch->sim, TRUE);
}
static void
ahci_process_request_sense(device_t dev, union ccb *ccb)
{
struct ahci_channel *ch = device_get_softc(dev);
int i;
ch->recoverycmd = 0;
i = ccb->ccb_h.recovery_slot;
if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) {
ch->hold[i]->ccb_h.status |= CAM_AUTOSNS_VALID;
} else {
ch->hold[i]->ccb_h.status &= ~CAM_STATUS_MASK;
ch->hold[i]->ccb_h.status |= CAM_AUTOSENSE_FAIL;
}
xpt_done(ch->hold[i]);
ch->hold[i] = NULL;
ch->numhslots--;
xpt_free_ccb(ccb);
xpt_release_simq(ch->sim, TRUE);
}
static void
ahci_start(device_t dev, int fbs)
{
@ -2519,6 +2577,7 @@ ahciaction(struct cam_sim *sim, union ccb *ccb)
ccb->ccb_h.status = CAM_SEL_TIMEOUT;
break;
}
ccb->ccb_h.recovery_type = RECOVERY_NONE;
/* Check for command collision. */
if (ahci_check_collision(dev, ccb)) {
/* Freeze command. */

View File

@ -408,7 +408,7 @@ struct ahci_channel {
int numtslots; /* Number of tagged slots */
int numtslotspd[16];/* Number of tagged slots per dev */
int numhslots; /* Number of holden slots */
int readlog; /* Our READ LOG active */
int recoverycmd; /* Our READ LOG active */
int fatalerr; /* Fatal error happend */
int lastslot; /* Last used slot */
int taggedtarget; /* Last tagged target */