@@ -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;
}
@@ -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;
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.busch@intel.com> --- hw/nvme.c | 25 +++++++++++++++++++++++++ hw/nvme.h | 2 ++ 2 files changed, 27 insertions(+), 0 deletions(-)