Patchwork [06/11] QEMU NVMe: Implement flush and dsm

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

Comments

Keith Busch - Feb. 27, 2013, 12:47 a.m.
Signed-off-by: Keith Busch <keith.busch@intel.com>
---
 hw/nvme.c |   72 +++++++++++++++++++++++++++++++++++++++++++++++++++++++------
 hw/nvme.h |    2 +
 2 files changed, 67 insertions(+), 7 deletions(-)

Patch

diff --git a/hw/nvme.c b/hw/nvme.c
index 69136e0..087fce9 100644
--- a/hw/nvme.c
+++ b/hw/nvme.c
@@ -327,7 +327,6 @@  static void nvme_rw_cb(void *opaque, int ret)
     n = sq->ctrl;
     cq = n->cq[sq->cqid];
     qemu_sglist_destroy(&req->qsg);
-    req->aiocb = NULL;
 
     nvme_update_stats(ns, req->nlb, req->rw);
     if (!req->rw) {
@@ -391,10 +390,28 @@  static void nvme_dsm_dealloc(NvmeNamespace *ns, uint64_t slba, uint64_t nlb)
     }
 }
 
+static void nvme_dsm_cb(void *opaque, int ret)
+{
+    NvmeRequest *req = opaque;
+    NvmeSQueue *sq = req->sq;
+    NvmeCtrl *n = sq->ctrl;
+    NvmeCQueue *cq = n->cq[sq->cqid];
+
+    if (ret && !req->cqe.status) {
+        req->cqe.status = NVME_INTERNAL_DEV_ERROR << 1;
+    }
+    if (!(--req->aio_count)) {
+        g_free(req->aiocb_dsm);
+        nvme_enqueue_req_completion(cq, req);
+    }
+}
+
 static uint16_t nvme_dsm(NvmeCtrl *n, NvmeNamespace *ns, NvmeCmd *cmd,
     NvmeRequest *req)
 {
     uint16_t nr = (cmd->cdw10 & 0xff) + 1;
+    uint8_t lba_index = NVME_ID_NS_FLBAS_INDEX(ns->id_ns.flbas);
+    uint8_t data_shift = ns->id_ns.lbaf[lba_index].ds;
     NvmeDsmRange range[nr];
 
     if (nvme_dma_prp(cmd->prp1, cmd->prp2, sizeof(range), n, (uint8_t *)range,
@@ -406,17 +423,55 @@  static uint16_t nvme_dsm(NvmeCtrl *n, NvmeNamespace *ns, NvmeCmd *cmd,
         int i;
         uint64_t slba;
         uint32_t nlb;
+        req->aiocb_dsm = g_malloc(nr * sizeof (*req->aiocb_dsm));
+        req->aio_count = nr;
         for (i = 0; i < nr; i++) {
             slba = range[i].slba;
             nlb = range[i].nlb;
             if (slba + nlb > ns->id_ns.ncap) {
-                return NVME_LBA_RANGE | NVME_DNR;
+                req->aio_count -= (nr - i);
+                if (req->aio_count) {
+                    req->cqe.status = NVME_LBA_RANGE | NVME_DNR;
+                    break;
+                }
+                else {
+                    g_free(req->aiocb_dsm);
+                    return NVME_LBA_RANGE | NVME_DNR;
+                }
             }
             nvme_dsm_dealloc(ns, slba, nlb);
-            /* TODO: send bdrv_aio_discard request */
+            req->aiocb_dsm[i] = bdrv_aio_discard(n->conf.bs,
+                ns->start_block + (slba << (data_shift - 9)),
+                (nlb + 1) << (data_shift - 9), nvme_dsm_cb, req);
         }
     }
-    return NVME_SUCCESS;
+    if (!req->aio_count) {
+        return NVME_SUCCESS;
+    }
+    return NVME_NO_COMPLETE;
+}
+
+static void nvme_flush_cb(void *opaque, int ret)
+{
+    NvmeRequest *req = opaque;
+    NvmeSQueue *sq = req->sq;
+    NvmeCtrl *n = sq->ctrl;
+    NvmeCQueue *cq = n->cq[sq->cqid];
+
+    if (!ret) {
+        req->cqe.status = NVME_SUCCESS << 1;
+    } else {
+        req->cqe.status = NVME_INTERNAL_DEV_ERROR << 1;
+    }
+    nvme_enqueue_req_completion(cq, req);
+}
+
+static uint16_t nvme_flush(NvmeCtrl *n, NvmeNamespace *ns, NvmeCmd *cmd,
+    NvmeRequest *req)
+{
+    req->ns = ns;
+    bdrv_aio_flush(n->conf.bs, nvme_flush_cb, req);
+    return NVME_NO_COMPLETE;
 }
 
 static uint16_t nvme_io_cmd(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req)
@@ -429,7 +484,7 @@  static uint16_t nvme_io_cmd(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req)
     ns = &n->namespaces[cmd->nsid - 1];
     switch (cmd->opcode) {
     case NVME_CMD_FLUSH:
-        return NVME_SUCCESS;
+        return nvme_flush(n, ns, cmd, req);
     case NVME_CMD_WRITE:
     case NVME_CMD_READ:
         return nvme_rw(n, ns, cmd, req);
@@ -466,8 +521,9 @@  static uint16_t nvme_del_sq(NvmeCtrl *n, NvmeCmd *cmd)
     sq = n->sq[c->qid];
     while (!QTAILQ_EMPTY(&sq->out_req_list)) {
         req = QTAILQ_FIRST(&sq->out_req_list);
-        assert(req->aiocb);
-        bdrv_aio_cancel(req->aiocb);
+        if (req->aiocb) {
+            bdrv_aio_cancel(req->aiocb);
+        }
     }
     if (!nvme_check_cqid(n, sq->cqid)) {
         NvmeCQueue *cq = n->cq[sq->cqid];
@@ -1069,6 +1125,8 @@  static void nvme_sq_process(void *opaque)
         QTAILQ_INSERT_TAIL(&sq->out_req_list, req, entry);
         memset(&req->cqe, 0, sizeof(req->cqe));
         req->cqe.cid = cmd.cid;
+        req->aiocb = NULL;
+        req->aiocb_dsm = NULL;
 
         status = sq->id ? nvme_io_cmd(n, &cmd, req) :
             nvme_admin_cmd(n, &cmd, req);
diff --git a/hw/nvme.h b/hw/nvme.h
index 964e91d..4dabb49 100644
--- a/hw/nvme.h
+++ b/hw/nvme.h
@@ -591,9 +591,11 @@  typedef struct NvmeRequest {
     struct NvmeSQueue       *sq;
     struct NvmeNamespace    *ns;
     BlockDriverAIOCB        *aiocb;
+    BlockDriverAIOCB        **aiocb_dsm;
     uint64_t                slba;
     uint16_t                rw;
     uint16_t                nlb;
+    uint16_t                aio_count;
     NvmeCqe                 cqe;
     QEMUSGList              qsg;
     QTAILQ_ENTRY(NvmeRequest)entry;