Support stopping and restarting the AHCI command list via toggling
PxCMD.ST from '1' to '0' and back. This allows the driver a chance to recover if for instance a timeout occurred due to activity on the host. Reviewed by: grehan
This commit is contained in:
parent
b021de9cd7
commit
3ef05c4677
@ -55,8 +55,7 @@ __FBSDID("$FreeBSD$");
|
||||
enum blockop {
|
||||
BOP_READ,
|
||||
BOP_WRITE,
|
||||
BOP_FLUSH,
|
||||
BOP_CANCEL
|
||||
BOP_FLUSH
|
||||
};
|
||||
|
||||
enum blockstat {
|
||||
@ -159,9 +158,6 @@ blockif_proc(struct blockif_ctxt *bc, struct blockif_elem *be)
|
||||
break;
|
||||
case BOP_FLUSH:
|
||||
break;
|
||||
case BOP_CANCEL:
|
||||
err = EINTR;
|
||||
break;
|
||||
default:
|
||||
err = EINVAL;
|
||||
break;
|
||||
@ -356,9 +352,28 @@ blockif_flush(struct blockif_ctxt *bc, struct blockif_req *breq)
|
||||
int
|
||||
blockif_cancel(struct blockif_ctxt *bc, struct blockif_req *breq)
|
||||
{
|
||||
struct blockif_elem *be;
|
||||
|
||||
assert(bc->bc_magic == BLOCKIF_SIG);
|
||||
return (blockif_request(bc, breq, BOP_CANCEL));
|
||||
|
||||
pthread_mutex_lock(&bc->bc_mtx);
|
||||
TAILQ_FOREACH(be, &bc->bc_inuseq, be_link) {
|
||||
if (be->be_req == breq)
|
||||
break;
|
||||
}
|
||||
if (be == NULL) {
|
||||
pthread_mutex_unlock(&bc->bc_mtx);
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
TAILQ_REMOVE(&bc->bc_inuseq, be, be_link);
|
||||
be->be_status = BST_FREE;
|
||||
be->be_req = NULL;
|
||||
TAILQ_INSERT_TAIL(&bc->bc_freeq, be, be_link);
|
||||
bc->bc_req_count--;
|
||||
pthread_mutex_unlock(&bc->bc_mtx);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
|
@ -48,6 +48,7 @@ __FBSDID("$FreeBSD$");
|
||||
#include <unistd.h>
|
||||
#include <assert.h>
|
||||
#include <pthread.h>
|
||||
#include <pthread_np.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "bhyverun.h"
|
||||
@ -115,7 +116,8 @@ static FILE *dbg;
|
||||
struct ahci_ioreq {
|
||||
struct blockif_req io_req;
|
||||
struct ahci_port *io_pr;
|
||||
STAILQ_ENTRY(ahci_ioreq) io_list;
|
||||
STAILQ_ENTRY(ahci_ioreq) io_flist;
|
||||
TAILQ_ENTRY(ahci_ioreq) io_blist;
|
||||
uint8_t *cfis;
|
||||
uint32_t len;
|
||||
uint32_t done;
|
||||
@ -160,6 +162,7 @@ struct ahci_port {
|
||||
struct ahci_ioreq *ioreq;
|
||||
int ioqsz;
|
||||
STAILQ_HEAD(ahci_fhead, ahci_ioreq) iofhd;
|
||||
TAILQ_HEAD(ahci_bhead, ahci_ioreq) iobhd;
|
||||
};
|
||||
|
||||
struct ahci_cmd_hdr {
|
||||
@ -359,6 +362,68 @@ ahci_write_reset_fis_d2h(struct ahci_port *p)
|
||||
ahci_write_fis(p, FIS_TYPE_REGD2H, fis);
|
||||
}
|
||||
|
||||
static void
|
||||
ahci_check_stopped(struct ahci_port *p)
|
||||
{
|
||||
/*
|
||||
* If we are no longer processing the command list and nothing
|
||||
* is in-flight, clear the running bit.
|
||||
*/
|
||||
if (!(p->cmd & AHCI_P_CMD_ST)) {
|
||||
if (p->pending == 0)
|
||||
p->cmd &= ~(AHCI_P_CMD_CR | AHCI_P_CMD_CCS_MASK);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
ahci_port_stop(struct ahci_port *p)
|
||||
{
|
||||
struct ahci_ioreq *aior;
|
||||
uint8_t *cfis;
|
||||
int slot;
|
||||
int ncq;
|
||||
int error;
|
||||
|
||||
assert(pthread_mutex_isowned_np(&p->pr_sc->mtx));
|
||||
|
||||
TAILQ_FOREACH(aior, &p->iobhd, io_blist) {
|
||||
/*
|
||||
* Try to cancel the outstanding blockif request.
|
||||
*/
|
||||
error = blockif_cancel(p->bctx, &aior->io_req);
|
||||
if (error != 0)
|
||||
continue;
|
||||
|
||||
slot = aior->slot;
|
||||
cfis = aior->cfis;
|
||||
if (cfis[2] == ATA_WRITE_FPDMA_QUEUED ||
|
||||
cfis[2] == ATA_READ_FPDMA_QUEUED)
|
||||
ncq = 1;
|
||||
|
||||
if (ncq)
|
||||
p->sact &= ~(1 << slot);
|
||||
else
|
||||
p->ci &= ~(1 << slot);
|
||||
|
||||
/*
|
||||
* This command is now done.
|
||||
*/
|
||||
p->pending &= ~(1 << slot);
|
||||
|
||||
/*
|
||||
* Delete the blockif request from the busy list
|
||||
*/
|
||||
TAILQ_REMOVE(&p->iobhd, aior, io_blist);
|
||||
|
||||
/*
|
||||
* Move the blockif request back to the free list
|
||||
*/
|
||||
STAILQ_INSERT_TAIL(&p->iofhd, aior, io_flist);
|
||||
}
|
||||
|
||||
ahci_check_stopped(p);
|
||||
}
|
||||
|
||||
static void
|
||||
ahci_port_reset(struct ahci_port *pr)
|
||||
{
|
||||
@ -492,7 +557,7 @@ ahci_handle_dma(struct ahci_port *p, int slot, uint8_t *cfis, uint32_t done,
|
||||
*/
|
||||
aior = STAILQ_FIRST(&p->iofhd);
|
||||
assert(aior != NULL);
|
||||
STAILQ_REMOVE_HEAD(&p->iofhd, io_list);
|
||||
STAILQ_REMOVE_HEAD(&p->iofhd, io_flist);
|
||||
aior->cfis = cfis;
|
||||
aior->slot = slot;
|
||||
aior->len = len;
|
||||
@ -503,14 +568,20 @@ ahci_handle_dma(struct ahci_port *p, int slot, uint8_t *cfis, uint32_t done,
|
||||
if (iovcnt > BLOCKIF_IOV_MAX) {
|
||||
aior->prdtl = iovcnt - BLOCKIF_IOV_MAX;
|
||||
iovcnt = BLOCKIF_IOV_MAX;
|
||||
/*
|
||||
* Mark this command in-flight.
|
||||
*/
|
||||
p->pending |= 1 << slot;
|
||||
} else
|
||||
aior->prdtl = 0;
|
||||
breq->br_iovcnt = iovcnt;
|
||||
|
||||
/*
|
||||
* Mark this command in-flight.
|
||||
*/
|
||||
p->pending |= 1 << slot;
|
||||
|
||||
/*
|
||||
* Stuff request onto busy list
|
||||
*/
|
||||
TAILQ_INSERT_HEAD(&p->iobhd, aior, io_blist);
|
||||
|
||||
/*
|
||||
* Build up the iovec based on the prdt
|
||||
*/
|
||||
@ -546,7 +617,7 @@ ahci_handle_flush(struct ahci_port *p, int slot, uint8_t *cfis)
|
||||
*/
|
||||
aior = STAILQ_FIRST(&p->iofhd);
|
||||
assert(aior != NULL);
|
||||
STAILQ_REMOVE_HEAD(&p->iofhd, io_list);
|
||||
STAILQ_REMOVE_HEAD(&p->iofhd, io_flist);
|
||||
aior->cfis = cfis;
|
||||
aior->slot = slot;
|
||||
aior->len = 0;
|
||||
@ -554,6 +625,16 @@ ahci_handle_flush(struct ahci_port *p, int slot, uint8_t *cfis)
|
||||
aior->prdtl = 0;
|
||||
breq = &aior->io_req;
|
||||
|
||||
/*
|
||||
* Mark this command in-flight.
|
||||
*/
|
||||
p->pending |= 1 << slot;
|
||||
|
||||
/*
|
||||
* Stuff request onto busy list
|
||||
*/
|
||||
TAILQ_INSERT_HEAD(&p->iobhd, aior, io_blist);
|
||||
|
||||
err = blockif_flush(p->bctx, breq);
|
||||
assert(err == 0);
|
||||
}
|
||||
@ -961,7 +1042,7 @@ atapi_read(struct ahci_port *p, int slot, uint8_t *cfis,
|
||||
*/
|
||||
aior = STAILQ_FIRST(&p->iofhd);
|
||||
assert(aior != NULL);
|
||||
STAILQ_REMOVE_HEAD(&p->iofhd, io_list);
|
||||
STAILQ_REMOVE_HEAD(&p->iofhd, io_flist);
|
||||
aior->cfis = cfis;
|
||||
aior->slot = slot;
|
||||
aior->len = len;
|
||||
@ -976,6 +1057,16 @@ atapi_read(struct ahci_port *p, int slot, uint8_t *cfis,
|
||||
aior->prdtl = 0;
|
||||
breq->br_iovcnt = iovcnt;
|
||||
|
||||
/*
|
||||
* Mark this command in-flight.
|
||||
*/
|
||||
p->pending |= 1 << slot;
|
||||
|
||||
/*
|
||||
* Stuff request onto busy list
|
||||
*/
|
||||
TAILQ_INSERT_HEAD(&p->iobhd, aior, io_blist);
|
||||
|
||||
/*
|
||||
* Build up the iovec based on the prdt
|
||||
*/
|
||||
@ -1414,10 +1505,15 @@ ata_ioreq_cb(struct blockif_req *br, int err)
|
||||
|
||||
pthread_mutex_lock(&sc->mtx);
|
||||
|
||||
/*
|
||||
* Delete the blockif request from the busy list
|
||||
*/
|
||||
TAILQ_REMOVE(&p->iobhd, aior, io_blist);
|
||||
|
||||
/*
|
||||
* Move the blockif request back to the free list
|
||||
*/
|
||||
STAILQ_INSERT_TAIL(&p->iofhd, aior, io_list);
|
||||
STAILQ_INSERT_TAIL(&p->iofhd, aior, io_flist);
|
||||
|
||||
if (pending && !err) {
|
||||
ahci_handle_dma(p, slot, cfis, aior->done,
|
||||
@ -1438,17 +1534,18 @@ ata_ioreq_cb(struct blockif_req *br, int err)
|
||||
p->serr |= (1 << slot);
|
||||
}
|
||||
|
||||
/*
|
||||
* This command is now complete.
|
||||
*/
|
||||
p->pending &= ~(1 << slot);
|
||||
|
||||
if (ncq) {
|
||||
p->sact &= ~(1 << slot);
|
||||
ahci_write_fis_sdb(p, slot, tfd);
|
||||
} else
|
||||
ahci_write_fis_d2h(p, slot, cfis, tfd);
|
||||
|
||||
/*
|
||||
* This command is now complete.
|
||||
*/
|
||||
p->pending &= ~(1 << slot);
|
||||
|
||||
ahci_check_stopped(p);
|
||||
out:
|
||||
pthread_mutex_unlock(&sc->mtx);
|
||||
DPRINTF("%s exit\n", __func__);
|
||||
@ -1477,10 +1574,15 @@ atapi_ioreq_cb(struct blockif_req *br, int err)
|
||||
|
||||
pthread_mutex_lock(&sc->mtx);
|
||||
|
||||
/*
|
||||
* Delete the blockif request from the busy list
|
||||
*/
|
||||
TAILQ_REMOVE(&p->iobhd, aior, io_blist);
|
||||
|
||||
/*
|
||||
* Move the blockif request back to the free list
|
||||
*/
|
||||
STAILQ_INSERT_TAIL(&p->iofhd, aior, io_list);
|
||||
STAILQ_INSERT_TAIL(&p->iofhd, aior, io_flist);
|
||||
|
||||
if (pending && !err) {
|
||||
atapi_read(p, slot, cfis, aior->done, hdr->prdtl - pending);
|
||||
@ -1500,6 +1602,12 @@ atapi_ioreq_cb(struct blockif_req *br, int err)
|
||||
cfis[4] = (cfis[4] & ~7) | ATA_I_CMD | ATA_I_IN;
|
||||
ahci_write_fis_d2h(p, slot, cfis, tfd);
|
||||
|
||||
/*
|
||||
* This command is now complete.
|
||||
*/
|
||||
p->pending &= ~(1 << slot);
|
||||
|
||||
ahci_check_stopped(p);
|
||||
out:
|
||||
pthread_mutex_unlock(&sc->mtx);
|
||||
DPRINTF("%s exit\n", __func__);
|
||||
@ -1526,8 +1634,10 @@ pci_ahci_ioreq_init(struct ahci_port *pr)
|
||||
else
|
||||
vr->io_req.br_callback = atapi_ioreq_cb;
|
||||
vr->io_req.br_param = vr;
|
||||
STAILQ_INSERT_TAIL(&pr->iofhd, vr, io_list);
|
||||
STAILQ_INSERT_TAIL(&pr->iofhd, vr, io_flist);
|
||||
}
|
||||
|
||||
TAILQ_INIT(&pr->iobhd);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -1565,9 +1675,7 @@ pci_ahci_port_write(struct pci_ahci_softc *sc, uint64_t offset, uint64_t value)
|
||||
p->cmd = value;
|
||||
|
||||
if (!(value & AHCI_P_CMD_ST)) {
|
||||
p->cmd &= ~(AHCI_P_CMD_CR | AHCI_P_CMD_CCS_MASK);
|
||||
p->ci = 0;
|
||||
p->sact = 0;
|
||||
ahci_port_stop(p);
|
||||
} else {
|
||||
uint64_t clb;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user