When errors are detected in commands, set the error log page so that the host driver may see relevent error data.
Signed-off-by: Keith Busch <keith.bu...@intel.com> --- hw/nvme.c | 25 +++++++++++++++++++++++++ hw/nvme.h | 2 ++ 2 files changed, 27 insertions(+), 0 deletions(-) diff --git a/hw/nvme.c b/hw/nvme.c index 087fce9..c65c179 100644 --- a/hw/nvme.c +++ b/hw/nvme.c @@ -234,6 +234,21 @@ static void nvme_enqueue_req_completion(NvmeCQueue *cq, NvmeRequest *req) qemu_mod_timer(cq->timer, qemu_get_clock_ns(vm_clock) + 500); } +static void nvme_set_error_page(NvmeCtrl *n, uint16_t sqid, uint16_t cid, + uint16_t status, uint16_t location, uint64_t lba, uint32_t nsid) +{ + NvmeErrorLog *elp; + elp = &n->elpes[n->elp_index]; + elp->error_count = n->error_count++; + elp->sqid = sqid; + elp->cid = cid; + elp->status_field = status; + elp->param_error_location = location; + elp->lba = lba; + elp->nsid = nsid; + n->elp_index = (n->elp_index + 1) % n->elpe; +} + static void nvme_enqueue_event(NvmeCtrl *n, uint8_t event_type, uint8_t event_info, uint8_t log_page) { @@ -352,9 +367,13 @@ static uint16_t nvme_rw(NvmeCtrl *n, NvmeNamespace *ns, NvmeCmd *cmd, uint16_t ret; if ((rw->slba + rw->nlb) > ns->id_ns.nsze) { + nvme_set_error_page(n, req->sq->id, cmd->cid, NVME_LBA_RANGE, + offsetof(NvmeRwCmd, slba), rw->slba + rw->nlb, ns->id); return NVME_LBA_RANGE | NVME_DNR; } if (n->id_ctrl.mdts && data_size > n->page_size * (1 << n->id_ctrl.mdts)) { + nvme_set_error_page(n, req->sq->id, cmd->cid, NVME_INVALID_FIELD, + offsetof(NvmeRwCmd, nlb), rw->slba + rw->nlb, ns->id); return NVME_INVALID_FIELD | NVME_DNR; } @@ -416,6 +435,8 @@ static uint16_t nvme_dsm(NvmeCtrl *n, NvmeNamespace *ns, NvmeCmd *cmd, if (nvme_dma_prp(cmd->prp1, cmd->prp2, sizeof(range), n, (uint8_t *)range, DMA_DIRECTION_TO_DEVICE)) { + nvme_set_error_page(n, req->sq->id, cmd->cid, NVME_INVALID_FIELD, + offsetof(NvmeCmd, prp1), 0, ns->id); return NVME_INVALID_FIELD | NVME_DNR; } @@ -430,6 +451,8 @@ static uint16_t nvme_dsm(NvmeCtrl *n, NvmeNamespace *ns, NvmeCmd *cmd, nlb = range[i].nlb; if (slba + nlb > ns->id_ns.ncap) { req->aio_count -= (nr - i); + nvme_set_error_page(n, req->sq->id, cmd->cid, NVME_LBA_RANGE, + offsetof(NvmeCmd, cdw10), slba + nlb, ns->id); if (req->aio_count) { req->cqe.status = NVME_LBA_RANGE | NVME_DNR; break; @@ -478,6 +501,8 @@ static uint16_t nvme_io_cmd(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req) { NvmeNamespace *ns; if (cmd->nsid == 0 || cmd->nsid > n->num_namespaces) { + nvme_set_error_page(n, req->sq->id, cmd->cid, NVME_INVALID_NSID, + offsetof(NvmeCmd, nsid), 0, cmd->nsid); return NVME_INVALID_NSID | NVME_DNR; } diff --git a/hw/nvme.h b/hw/nvme.h index 4dabb49..53c0f39 100644 --- a/hw/nvme.h +++ b/hw/nvme.h @@ -674,6 +674,8 @@ typedef struct NvmeCtrl { uint8_t aerl; uint8_t acl; uint8_t elpe; + uint8_t elp_index; + uint8_t error_count; uint8_t mdts; uint8_t cqr; uint8_t max_sqes; -- 1.7.0.4