summaryrefslogtreecommitdiffstats
path: root/usr.sbin
diff options
context:
space:
mode:
authormav <mav@FreeBSD.org>2015-05-13 10:35:34 +0000
committermav <mav@FreeBSD.org>2015-05-13 10:35:34 +0000
commit4c8cc209abd9d3d8663cc45eab899b756d73ae3d (patch)
tree16aefdb39bb232fcf72b4da8055f4825b8e1fbce /usr.sbin
parent8f115fa59b840ac3a04bef1ea148d0b1a80a2dfd (diff)
downloadFreeBSD-src-4c8cc209abd9d3d8663cc45eab899b756d73ae3d.zip
FreeBSD-src-4c8cc209abd9d3d8663cc45eab899b756d73ae3d.tar.gz
MFC r282364, r282429, r282524:
Implement BSY reporting and in-order execution of non-NCQ commands. Block command queue on BSY or DRQ bits set or device error reported. This can be a performance penalization for non-NCQ commands, but it is required for proper error recovery and standard compliance.
Diffstat (limited to 'usr.sbin')
-rw-r--r--usr.sbin/bhyve/pci_ahci.c80
1 files changed, 60 insertions, 20 deletions
diff --git a/usr.sbin/bhyve/pci_ahci.c b/usr.sbin/bhyve/pci_ahci.c
index 0dcf5bb..9dbabcd 100644
--- a/usr.sbin/bhyve/pci_ahci.c
+++ b/usr.sbin/bhyve/pci_ahci.c
@@ -135,11 +135,13 @@ struct ahci_port {
char ident[20 + 1];
int atapi;
int reset;
+ int waitforclear;
int mult_sectors;
uint8_t xfermode;
uint8_t err_cfis[20];
uint8_t sense_key;
uint8_t asc;
+ u_int ccs;
uint32_t pending;
uint32_t clb;
@@ -204,6 +206,8 @@ struct pci_ahci_softc {
};
#define ahci_ctx(sc) ((sc)->asc_pi->pi_vmctx)
+static void ahci_handle_port(struct ahci_port *p);
+
static inline void lba_to_msf(uint8_t *buf, int lba)
{
lba += 150;
@@ -269,22 +273,26 @@ ahci_write_fis(struct ahci_port *p, enum sata_fis_type ft, uint8_t *fis)
case FIS_TYPE_REGD2H:
offset = 0x40;
len = 20;
- irq = AHCI_P_IX_DHR;
+ irq = (fis[1] & (1 << 6)) ? AHCI_P_IX_DHR : 0;
break;
case FIS_TYPE_SETDEVBITS:
offset = 0x58;
len = 8;
- irq = AHCI_P_IX_SDB;
+ irq = (fis[1] & (1 << 6)) ? AHCI_P_IX_SDB : 0;
break;
case FIS_TYPE_PIOSETUP:
offset = 0x20;
len = 20;
- irq = 0;
+ irq = (fis[1] & (1 << 6)) ? AHCI_P_IX_PS : 0;
break;
default:
WPRINTF("unsupported fis type %d\n", ft);
return;
}
+ if (fis[2] & ATA_S_ERROR) {
+ p->waitforclear = 1;
+ irq |= AHCI_P_IX_TFE;
+ }
memcpy(p->rfis + offset, fis, len);
if (irq) {
p->is |= irq;
@@ -309,22 +317,23 @@ ahci_write_fis_sdb(struct ahci_port *p, int slot, uint8_t *cfis, uint32_t tfd)
uint8_t error;
error = (tfd >> 8) & 0xff;
+ tfd &= 0x77;
memset(fis, 0, sizeof(fis));
fis[0] = FIS_TYPE_SETDEVBITS;
fis[1] = (1 << 6);
- fis[2] = tfd & 0x77;
+ fis[2] = tfd;
fis[3] = error;
if (fis[2] & ATA_S_ERROR) {
- p->is |= AHCI_P_IX_TFE;
p->err_cfis[0] = slot;
- p->err_cfis[2] = tfd & 0x77;
+ p->err_cfis[2] = tfd;
p->err_cfis[3] = error;
memcpy(&p->err_cfis[4], cfis + 4, 16);
} else {
*(uint32_t *)(fis + 4) = (1 << slot);
p->sact &= ~(1 << slot);
}
- p->tfd = tfd;
+ p->tfd &= ~0x77;
+ p->tfd |= tfd;
ahci_write_fis(p, FIS_TYPE_SETDEVBITS, fis);
}
@@ -351,7 +360,6 @@ ahci_write_fis_d2h(struct ahci_port *p, int slot, uint8_t *cfis, uint32_t tfd)
fis[12] = cfis[12];
fis[13] = cfis[13];
if (fis[2] & ATA_S_ERROR) {
- p->is |= AHCI_P_IX_TFE;
p->err_cfis[0] = 0x80;
p->err_cfis[2] = tfd & 0xff;
p->err_cfis[3] = error;
@@ -363,6 +371,21 @@ ahci_write_fis_d2h(struct ahci_port *p, int slot, uint8_t *cfis, uint32_t tfd)
}
static void
+ahci_write_fis_d2h_ncq(struct ahci_port *p, int slot)
+{
+ uint8_t fis[20];
+
+ p->tfd = ATA_S_READY | ATA_S_DSC;
+ memset(fis, 0, sizeof(fis));
+ fis[0] = FIS_TYPE_REGD2H;
+ fis[1] = 0; /* No interrupt */
+ fis[2] = p->tfd; /* Status */
+ fis[3] = 0; /* No error */
+ p->ci &= ~(1 << slot);
+ ahci_write_fis(p, FIS_TYPE_REGD2H, fis);
+}
+
+static void
ahci_write_reset_fis_d2h(struct ahci_port *p)
{
uint8_t fis[20];
@@ -389,9 +412,11 @@ ahci_check_stopped(struct ahci_port *p)
*/
if (!(p->cmd & AHCI_P_CMD_ST)) {
if (p->pending == 0) {
+ p->ccs = 0;
p->cmd &= ~(AHCI_P_CMD_CR | AHCI_P_CMD_CCS_MASK);
p->ci = 0;
p->sact = 0;
+ p->waitforclear = 0;
}
}
}
@@ -589,12 +614,13 @@ ahci_handle_rw(struct ahci_port *p, int slot, uint8_t *cfis, uint32_t done)
struct ahci_cmd_hdr *hdr;
uint64_t lba;
uint32_t len;
- int err, ncq, readop;
+ int err, first, ncq, readop;
prdt = (struct ahci_prdt_entry *)(cfis + 0x80);
hdr = (struct ahci_cmd_hdr *)(p->cmd_lst + slot * AHCI_CL_SIZE);
ncq = 0;
readop = 1;
+ first = (done == 0);
if (cfis[2] == ATA_WRITE || cfis[2] == ATA_WRITE48 ||
cfis[2] == ATA_WRITE_MUL || cfis[2] == ATA_WRITE_MUL48 ||
@@ -655,14 +681,14 @@ ahci_handle_rw(struct ahci_port *p, int slot, uint8_t *cfis, uint32_t done)
/* Stuff request onto busy list. */
TAILQ_INSERT_HEAD(&p->iobhd, aior, io_blist);
+ if (ncq && first)
+ ahci_write_fis_d2h_ncq(p, slot);
+
if (readop)
err = blockif_read(p->bctx, breq);
else
err = blockif_write(p->bctx, breq);
assert(err == 0);
-
- if (ncq)
- p->ci &= ~(1 << slot);
}
static void
@@ -735,15 +761,18 @@ ahci_handle_dsm_trim(struct ahci_port *p, int slot, uint8_t *cfis, uint32_t done
uint8_t *entry;
uint64_t elba;
uint32_t len, elen;
- int err;
+ int err, first, ncq;
uint8_t buf[512];
+ first = (done == 0);
if (cfis[2] == ATA_DATA_SET_MANAGEMENT) {
len = (uint16_t)cfis[13] << 8 | cfis[12];
len *= 512;
+ ncq = 0;
} else { /* ATA_SEND_FPDMA_QUEUED */
len = (uint16_t)cfis[11] << 8 | cfis[3];
len *= 512;
+ ncq = 1;
}
read_prdt(p, slot, cfis, buf, sizeof(buf));
@@ -762,6 +791,8 @@ next:
ahci_write_fis_d2h(p, slot, cfis, ATA_S_READY | ATA_S_DSC);
p->pending &= ~(1 << slot);
ahci_check_stopped(p);
+ if (!first)
+ ahci_handle_port(p);
return;
}
goto next;
@@ -793,6 +824,9 @@ next:
*/
TAILQ_INSERT_HEAD(&p->iobhd, aior, io_blist);
+ if (ncq && first)
+ ahci_write_fis_d2h_ncq(p, slot);
+
err = blockif_delete(p->bctx, breq);
assert(err == 0);
}
@@ -1548,6 +1582,7 @@ static void
ahci_handle_cmd(struct ahci_port *p, int slot, uint8_t *cfis)
{
+ p->tfd |= ATA_S_BUSY;
switch (cfis[2]) {
case ATA_ATA_IDENTIFY:
handle_identify(p, slot, cfis);
@@ -1729,20 +1764,23 @@ ahci_handle_slot(struct ahci_port *p, int slot)
static void
ahci_handle_port(struct ahci_port *p)
{
- int i;
if (!(p->cmd & AHCI_P_CMD_ST))
return;
/*
* Search for any new commands to issue ignoring those that
- * are already in-flight.
+ * are already in-flight. Stop if device is busy or in error.
*/
- for (i = 0; (i < 32) && p->ci; i++) {
- if ((p->ci & (1 << i)) && !(p->pending & (1 << i))) {
+ for (; (p->ci & ~p->pending) != 0; p->ccs = ((p->ccs + 1) & 31)) {
+ if ((p->tfd & (ATA_S_BUSY | ATA_S_DRQ)) != 0)
+ break;
+ if (p->waitforclear)
+ break;
+ if ((p->ci & ~p->pending & (1 << p->ccs)) != 0) {
p->cmd &= ~AHCI_P_CMD_CCS_MASK;
- p->cmd |= i << AHCI_P_CMD_CCS_SHIFT;
- ahci_handle_slot(p, i);
+ p->cmd |= p->ccs << AHCI_P_CMD_CCS_SHIFT;
+ ahci_handle_slot(p, p->ccs);
}
}
}
@@ -1819,6 +1857,7 @@ ata_ioreq_cb(struct blockif_req *br, int err)
p->pending &= ~(1 << slot);
ahci_check_stopped(p);
+ ahci_handle_port(p);
out:
pthread_mutex_unlock(&sc->mtx);
DPRINTF("%s exit\n", __func__);
@@ -1880,6 +1919,7 @@ atapi_ioreq_cb(struct blockif_req *br, int err)
p->pending &= ~(1 << slot);
ahci_check_stopped(p);
+ ahci_handle_port(p);
out:
pthread_mutex_unlock(&sc->mtx);
DPRINTF("%s exit\n", __func__);
@@ -1976,7 +2016,7 @@ pci_ahci_port_write(struct pci_ahci_softc *sc, uint64_t offset, uint64_t value)
}
if (value & AHCI_P_CMD_CLO) {
- p->tfd = 0;
+ p->tfd &= ~(ATA_S_BUSY | ATA_S_DRQ);
p->cmd &= ~AHCI_P_CMD_CLO;
}
OpenPOWER on IntegriCloud