Patchwork [07/11] QEMU NVMe: Set error pages with error data

login
register
mail settings
Submitter Keith Busch
Date Feb. 27, 2013, 12:47 a.m.
Message ID <1361926034-21824-8-git-send-email-keith.busch@intel.com>
Download mbox | patch
Permalink /patch/223455/
State New
Headers show

Comments

Keith Busch - Feb. 27, 2013, 12:47 a.m.
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(-)

Patch

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;