[RFC PATCH 2/2] virtio-nvme(qemu): NVMe device using virtio

Ming Lin mlin at kernel.org
Wed Sep 9 22:48:32 PDT 2015


Play it with:
-drive file=disk.img,format=raw,if=none,id=D22 \
-device virtio-nvme-pci,drive=D22,serial=1234,num_queues=4

Signed-off-by: Ming Lin <ming.l at ssi.samsung.com>
---
 hw/block/Makefile.objs                       |   2 +-
 hw/block/virtio-nvme.c                       | 449 +++++++++++++++++++++++++++
 hw/virtio/virtio-pci.c                       |  42 +++
 hw/virtio/virtio-pci.h                       |  14 +
 include/hw/pci/pci.h                         |   1 +
 include/hw/virtio/virtio-nvme.h              |  60 ++++
 include/standard-headers/linux/virtio_ids.h  |   1 +
 include/standard-headers/linux/virtio_nvme.h |  16 +
 8 files changed, 584 insertions(+), 1 deletion(-)
 create mode 100644 hw/block/virtio-nvme.c
 create mode 100644 include/hw/virtio/virtio-nvme.h
 create mode 100644 include/standard-headers/linux/virtio_nvme.h

diff --git a/hw/block/Makefile.objs b/hw/block/Makefile.objs
index d4c3ab7..a6e0b1c 100644
--- a/hw/block/Makefile.objs
+++ b/hw/block/Makefile.objs
@@ -11,5 +11,5 @@ common-obj-$(CONFIG_NVME_PCI) += nvme.o
 
 obj-$(CONFIG_SH4) += tc58128.o
 
-obj-$(CONFIG_VIRTIO) += virtio-blk.o
+obj-$(CONFIG_VIRTIO) += virtio-blk.o virtio-nvme.o
 obj-$(CONFIG_VIRTIO) += dataplane/
diff --git a/hw/block/virtio-nvme.c b/hw/block/virtio-nvme.c
new file mode 100644
index 0000000..14ecfbc
--- /dev/null
+++ b/hw/block/virtio-nvme.c
@@ -0,0 +1,449 @@
+#include <hw/pci/pci.h>
+#include "hw/virtio/virtio.h"
+#include "qemu-common.h"
+#include "qemu/iov.h"
+#include "qemu/error-report.h"
+#include "hw/block/block.h"
+#include "hw/virtio/virtio-access.h"
+
+#include "standard-headers/linux/virtio_ids.h"
+#include "standard-headers/linux/virtio_nvme.h"
+#include "nvme.h"
+#include "hw/virtio/virtio-nvme.h"
+
+#define VIRTIO_NVME_VQ_SIZE	128
+
+static void virtio_nvme_free_request(VirtIONVMEReq *req)
+{
+    if (req) {
+        g_slice_free(VirtIONVMEReq, req);
+    }
+}
+
+static uint16_t virtio_nvme_set_feature(VirtIONVME *n, VirtIONVMEReq *req)
+{
+    NvmeCmd *cmd = &req->cmd;
+    uint32_t dw10 = le32_to_cpu(cmd->cdw10);
+    uint32_t dw11 = le32_to_cpu(cmd->cdw11);
+
+    switch (dw10) {
+    case NVME_VOLATILE_WRITE_CACHE:
+        blk_set_enable_write_cache(n->conf.conf.blk, dw11 & 1);
+        break;
+    case NVME_NUMBER_OF_QUEUES:
+        req->resp->result =
+            cpu_to_le32((n->conf.num_queues - 1) | ((n->conf.num_queues - 1) << 16));
+        break;
+    default:
+        return NVME_INVALID_FIELD | NVME_DNR;
+    }
+    return NVME_SUCCESS;
+}
+
+static uint16_t virtio_nvme_identify(VirtIONVME *n, VirtIONVMEReq *req)
+{
+    NvmeNamespace *ns;
+    NvmeIdentify *c = (NvmeIdentify *)&req->cmd;
+    uint32_t cns  = le32_to_cpu(c->cns);
+    uint32_t nsid = le32_to_cpu(c->nsid);
+
+    if (cns) {
+        NvmeIdCtrl *id = &n->id_ctrl;
+
+        if (req->qiov.size != sizeof(NvmeIdCtrl))
+            return NVME_INVALID_FIELD;
+
+        strpadcpy((char *)id->mn, sizeof(id->mn), "QEMU Virtio NVMe Ctrl", ' ');
+        qemu_iovec_from_buf(&req->qiov, 0, (uint8_t *)&n->id_ctrl, sizeof(n->id_ctrl));
+        return 0;
+    }
+
+    if (nsid == 0 || nsid > n->num_namespaces)
+        return NVME_INVALID_NSID | NVME_DNR;
+
+    if (req->qiov.size != sizeof(NvmeIdNs))
+        return NVME_INVALID_FIELD;
+
+    ns = &n->namespaces[nsid - 1];
+    qemu_iovec_from_buf(&req->qiov, 0, (uint8_t *)&ns->id_ns, sizeof(ns->id_ns));
+    return 0;
+}
+
+static void virtio_nvme_complete_req(void *opaque, int ret)
+{
+    VirtIONVMEReq *req = opaque;
+    VirtIONVME *s = req->dev;
+    VirtIODevice *vdev = VIRTIO_DEVICE(s);
+
+    stw_p(&req->resp->status, ret);
+    virtqueue_push(req->vq, &req->elem, sizeof(*req->resp));
+    virtio_notify(vdev, req->vq);
+    virtio_nvme_free_request(req);
+}
+
+static uint16_t virtio_nvme_rw(VirtIONVMEReq *req)
+{
+    VirtIONVME *n = req->dev;
+    NvmeNamespace *ns;
+    NvmeRwCmd *rw = (NvmeRwCmd *)&req->cmd;
+    uint32_t nsid, nlb, slba;
+    uint8_t lba_index;
+    uint8_t data_shift;
+    uint64_t data_size;
+    uint64_t aio_slba;
+    int is_write;
+
+    nsid = le32_to_cpu(rw->nsid);
+    if (nsid == 0 || nsid > n->num_namespaces) {
+        return NVME_INVALID_NSID | NVME_DNR;
+    }
+
+    ns = &n->namespaces[nsid - 1];
+    nlb = le32_to_cpu(rw->nlb) + 1;
+    slba = le64_to_cpu(rw->slba);
+    lba_index  = NVME_ID_NS_FLBAS_INDEX(ns->id_ns.flbas);
+    data_shift = ns->id_ns.lbaf[lba_index].ds;
+    data_size = (uint64_t)nlb << data_shift;
+    aio_slba  = slba << (data_shift - BDRV_SECTOR_BITS);
+    is_write = rw->opcode == NVME_CMD_WRITE ? 1 : 0;
+
+    if ((slba + nlb) > ns->id_ns.nsze) {
+        return NVME_LBA_RANGE | NVME_DNR;
+    }
+
+    if (is_write)
+        blk_aio_writev(n->conf.conf.blk, aio_slba, &req->qiov, data_size>>BDRV_SECTOR_BITS,
+                       virtio_nvme_complete_req, req);
+    else
+        blk_aio_readv(n->conf.conf.blk, aio_slba, &req->qiov, data_size>>BDRV_SECTOR_BITS,
+                       virtio_nvme_complete_req, req);
+
+    return NVME_NO_COMPLETE;
+}
+
+static void virtio_nvme_handle_req_common(VirtIONVME *s, VirtIONVMEReq *req)
+{
+    struct iovec *in_iov = req->elem.in_sg;
+    struct iovec *iov = req->elem.out_sg;
+    unsigned in_num = req->elem.in_num;
+    unsigned out_num = req->elem.out_num;
+    int ret;
+
+    if (req->elem.out_num < 1 || req->elem.in_num < 1) {
+        error_report("virtio-nvme missing headers");
+        exit(1);
+    }
+
+    /* get cmd */
+    if (unlikely(iov_to_buf(iov, out_num, 0, &req->cmd,
+                            sizeof(req->cmd)) != sizeof(req->cmd))) {
+        error_report("virtio-nvme request cmd too short");
+        exit(1);
+    }
+
+    iov_discard_front(&iov, &out_num, sizeof(req->cmd));
+
+    if (in_iov[in_num - 1].iov_len < sizeof(struct virtio_nvme_resp)) {
+        error_report("virtio-nvme response too short");
+        exit(1);
+    }
+
+    /* get response */
+    req->resp = (void *)in_iov[in_num - 1].iov_base
+                + in_iov[in_num - 1].iov_len
+                - sizeof(struct virtio_nvme_resp);
+    iov_discard_back(in_iov, &in_num, sizeof(struct virtio_nvme_resp));
+
+    if (out_num)
+        qemu_iovec_init_external(&req->qiov, iov, out_num);
+    else if(in_num)
+        qemu_iovec_init_external(&req->qiov, in_iov, in_num);
+
+    switch (req->cmd.opcode) {
+    case NVME_ADM_CMD_IDENTIFY:
+        ret = virtio_nvme_identify(s, req);
+        break;
+    case NVME_ADM_CMD_SET_FEATURES:
+        ret = virtio_nvme_set_feature(s, req);
+        break;
+    case NVME_CMD_WRITE:
+    case NVME_CMD_READ:
+        ret = virtio_nvme_rw(req);
+        return;
+    default: /* TODO */
+        ret = NVME_INVALID_OPCODE | NVME_DNR;
+        break;
+    }
+
+    virtio_nvme_complete_req(req, ret);
+}
+
+static VirtIONVMEReq *virtio_nvme_alloc_request(VirtIONVME *s, VirtQueue *vq)
+{
+    VirtIONVMEReq *req = g_slice_new(VirtIONVMEReq);
+    req->dev = s;
+    req->vq = vq;
+    return req;
+}
+
+static VirtIONVMEReq *virtio_nvme_get_request(VirtIONVME *s, VirtQueue *vq)
+{
+    VirtIONVMEReq *req = virtio_nvme_alloc_request(s, vq);
+
+    if (!virtqueue_pop(vq, &req->elem)) {
+        virtio_nvme_free_request(req);
+        return NULL;
+    }
+
+    return req;
+}
+
+static void virtio_nvme_handle_req(VirtIODevice *vdev, VirtQueue *vq)
+{
+    VirtIONVME *s = VIRTIO_NVME(vdev);
+    VirtIONVMEReq *req;
+
+    while ((req = virtio_nvme_get_request(s, vq))) {
+        virtio_nvme_handle_req_common(s, req);
+    }
+}
+
+static void virtio_nvme_clear_ctrl(VirtIONVME *n)
+{
+    blk_flush(n->conf.conf.blk);
+    n->bar.cc = 0;
+}
+
+static int virtio_nvme_start_ctrl(VirtIONVME *n)
+{
+    uint32_t page_bits = NVME_CC_MPS(n->bar.cc) + 12;
+    VirtIODevice *vdev = (VirtIODevice *)n;
+    int i;
+
+    n->page_bits = page_bits;
+    n->page_size = 1 << n->page_bits;
+    n->max_prp_ents = n->page_size / sizeof(uint64_t);
+    n->cqe_size = 1 << NVME_CC_IOCQES(n->bar.cc);
+    n->sqe_size = 1 << NVME_CC_IOSQES(n->bar.cc);
+
+    n->admin_vq = virtio_add_queue(vdev, VIRTIO_NVME_VQ_SIZE, virtio_nvme_handle_req);
+
+    n->io_vqs = g_new0(VirtQueue *, n->conf.num_queues);
+    for (i = 0; i < n->conf.num_queues; i++)
+        n->io_vqs[i] = virtio_add_queue(vdev, VIRTIO_NVME_VQ_SIZE, virtio_nvme_handle_req);
+
+    return 0;
+}
+
+static int virtio_nvme_init(VirtIONVME *n)
+{
+    NvmeIdCtrl *id = &n->id_ctrl;
+
+    int i;
+    int64_t bs_size;
+
+    if (!n->conf.conf.blk) {
+        return -1;
+    }
+
+    bs_size = blk_getlength(n->conf.conf.blk);
+    if (bs_size < 0) {
+        return -1;
+    }
+
+    blkconf_serial(&n->conf.conf, &n->serial);
+    if (!n->serial) {
+        return -1;
+    }
+    blkconf_blocksizes(&n->conf.conf);
+
+    n->num_namespaces = 1;
+    n->reg_size = 1 << qemu_fls(0x1004 + 2 * (n->conf.num_queues + 1) * 4);
+    n->ns_size = bs_size / (uint64_t)n->num_namespaces;
+
+    n->namespaces = g_new0(NvmeNamespace, n->num_namespaces);
+
+    strpadcpy((char *)id->mn, sizeof(id->mn), "QEMU NVMe Ctrl", ' ');
+    strpadcpy((char *)id->fr, sizeof(id->fr), "1.0", ' ');
+    strpadcpy((char *)id->sn, sizeof(id->sn), n->serial, ' ');
+    id->rab = 6;
+    id->ieee[0] = 0x00;
+    id->ieee[1] = 0x02;
+    id->ieee[2] = 0xb3;
+    id->oacs = cpu_to_le16(0);
+    id->frmw = 7 << 1;
+    id->lpa = 1 << 0;
+    id->sqes = (0x6 << 4) | 0x6;
+    id->cqes = (0x4 << 4) | 0x4;
+    id->nn = cpu_to_le32(n->num_namespaces);
+    id->psd[0].mp = cpu_to_le16(0x9c4);
+    id->psd[0].enlat = cpu_to_le32(0x10);
+    id->psd[0].exlat = cpu_to_le32(0x4);
+    if (blk_enable_write_cache(n->conf.conf.blk)) {
+        id->vwc = 1;
+    }
+
+    n->bar.cap = 0;
+    NVME_CAP_SET_MQES(n->bar.cap, 0x7ff);
+    NVME_CAP_SET_CQR(n->bar.cap, 1);
+    NVME_CAP_SET_AMS(n->bar.cap, 1);
+    NVME_CAP_SET_TO(n->bar.cap, 0xf);
+    NVME_CAP_SET_CSS(n->bar.cap, 1);
+    NVME_CAP_SET_MPSMAX(n->bar.cap, 4);
+
+    n->bar.vs = 0x00010100;
+    n->bar.intmc = n->bar.intms = 0;
+
+    for (i = 0; i < n->num_namespaces; i++) {
+        NvmeNamespace *ns = &n->namespaces[i];
+        NvmeIdNs *id_ns = &ns->id_ns;
+        id_ns->nsfeat = 0;
+        id_ns->nlbaf = 0;
+        id_ns->flbas = 0;
+        id_ns->mc = 0;
+        id_ns->dpc = 0;
+        id_ns->dps = 0;
+        id_ns->lbaf[0].ds = BDRV_SECTOR_BITS;
+        id_ns->ncap  = id_ns->nuse = id_ns->nsze =
+            cpu_to_le64(n->ns_size >>
+                id_ns->lbaf[NVME_ID_NS_FLBAS_INDEX(ns->id_ns.flbas)].ds);
+    }
+    return 0;
+}
+
+static void virtio_nvme_exit(VirtIONVME *n)
+{
+    virtio_nvme_clear_ctrl(n);
+    g_free(n->namespaces);
+}
+
+static void virtio_nvme_device_realize(DeviceState *dev, Error **errp)
+{
+    VirtIODevice *vdev = VIRTIO_DEVICE(dev);
+    VirtIONVME *n = VIRTIO_NVME(vdev);
+
+    virtio_init(vdev, "virtio-nvme", VIRTIO_ID_NVME,
+                sizeof(struct virtio_nvme_config));
+
+    n->blk = n->conf.conf.blk;
+
+    virtio_nvme_init(n);
+}
+
+static void virtio_nvme_device_unrealize(DeviceState *dev, Error **errp)
+{
+    VirtIODevice *vdev = VIRTIO_DEVICE(dev);
+    VirtIONVME *n = VIRTIO_NVME(dev);
+
+    virtio_nvme_exit(n);
+    virtio_cleanup(vdev);
+}
+
+static uint64_t virtio_nvme_get_features(VirtIODevice *vdev, uint64_t features)
+{
+    virtio_add_feature(&features, VIRTIO_NVME_F_SEG_MAX);
+    virtio_add_feature(&features, VIRTIO_NVME_F_MQ);
+
+    return features;
+}
+
+static void virtio_nvme_ctrl_config(VirtIONVME *n, uint64_t data)
+{
+    if (NVME_CC_EN(data) && !NVME_CC_EN(n->bar.cc)) {
+        n->bar.cc = data;
+        if (virtio_nvme_start_ctrl(n)) {
+            n->bar.csts = NVME_CSTS_FAILED;
+        } else {
+            n->bar.csts = NVME_CSTS_READY;
+        }
+    } else if (!NVME_CC_EN(data) && NVME_CC_EN(n->bar.cc)) {
+        virtio_nvme_clear_ctrl(n);
+        n->bar.csts &= ~NVME_CSTS_READY;
+    }
+    if (NVME_CC_SHN(data) && !(NVME_CC_SHN(n->bar.cc))) {
+            virtio_nvme_clear_ctrl(n);
+            n->bar.cc = data;
+            n->bar.csts |= NVME_CSTS_SHST_COMPLETE;
+    } else if (!NVME_CC_SHN(data) && NVME_CC_SHN(n->bar.cc)) {
+            n->bar.csts &= ~NVME_CSTS_SHST_COMPLETE;
+            n->bar.cc = data;
+    }
+}
+
+static void virtio_nvme_get_config(VirtIODevice *vdev, uint8_t *config)
+{
+    VirtIONVME *s = VIRTIO_NVME(vdev);
+    struct virtio_nvme_config nvmecfg;
+
+    memset(&nvmecfg, 0, sizeof(nvmecfg));
+
+    virtio_stl_p(vdev, &nvmecfg.ctrl_config, s->bar.cc);
+    virtio_stl_p(vdev, &nvmecfg.csts, s->bar.csts);
+    virtio_stl_p(vdev, &nvmecfg.seg_max, 128 - 2);
+    virtio_stl_p(vdev, &nvmecfg.num_queues, s->conf.num_queues);
+
+    memcpy(config, &nvmecfg, sizeof(struct virtio_nvme_config));
+}
+
+static void virtio_nvme_set_config(VirtIODevice *vdev, const uint8_t *config)
+{
+    VirtIONVME *n = VIRTIO_NVME(vdev);
+    struct virtio_nvme_config nvmecfg;
+
+    memcpy(&nvmecfg, config, sizeof(nvmecfg));
+
+    virtio_nvme_ctrl_config(n, nvmecfg.ctrl_config);
+}
+
+static Property virtio_nvme_props[] = {
+    DEFINE_BLOCK_PROPERTIES(VirtIONVME, conf.conf),
+    DEFINE_PROP_STRING("serial", VirtIONVME, serial),
+    DEFINE_PROP_UINT32("num_queues", VirtIONVME, conf.num_queues, 1),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static const VMStateDescription virtio_nvme_vmstate = {
+    .name = "virtio_nvme",
+    .unmigratable = 1,
+};
+
+static void virtio_nvme_class_init(ObjectClass *oc, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(oc);
+    VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(oc);
+
+    set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
+    dc->desc = "Virtio NVMe";
+    dc->props = virtio_nvme_props;
+    dc->vmsd = &virtio_nvme_vmstate;
+
+    vdc->realize = virtio_nvme_device_realize;
+    vdc->unrealize = virtio_nvme_device_unrealize;
+    vdc->get_config = virtio_nvme_get_config;
+    vdc->set_config = virtio_nvme_set_config;
+    vdc->get_features = virtio_nvme_get_features;
+}
+
+static void virtio_nvme_instance_init(Object *obj)
+{
+    VirtIONVME *s = VIRTIO_NVME(obj);
+
+    device_add_bootindex_property(obj, &s->conf.conf.bootindex,
+                                  "bootindex", "/disk at 0,0",
+                                  DEVICE(obj), NULL);
+}
+
+static const TypeInfo virtio_nvme_info = {
+    .name          = TYPE_VIRTIO_NVME,
+    .parent        = TYPE_VIRTIO_DEVICE,
+    .instance_size = sizeof(VirtIONVME),
+    .class_init    = virtio_nvme_class_init,
+    .instance_init = virtio_nvme_instance_init,
+};
+
+static void virtio_nvme_register_types(void)
+{
+    type_register_static(&virtio_nvme_info);
+}
+
+type_init(virtio_nvme_register_types)
diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c
index 283401a..596dfa1 100644
--- a/hw/virtio/virtio-pci.c
+++ b/hw/virtio/virtio-pci.c
@@ -1682,6 +1682,47 @@ static const TypeInfo virtio_blk_pci_info = {
     .class_init    = virtio_blk_pci_class_init,
 };
 
+/* virtio-nvme-pci */
+
+static void virtio_nvme_pci_instance_init(Object *obj)
+{
+    VirtIONVMEPCI *dev = VIRTIO_NVME_PCI(obj);
+
+    virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
+                                TYPE_VIRTIO_NVME);
+}
+
+static void virtio_nvme_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp)
+{
+    VirtIONVMEPCI *dev = VIRTIO_NVME_PCI(vpci_dev);
+    DeviceState *vdev = DEVICE(&dev->vdev);
+
+    qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus));
+    object_property_set_bool(OBJECT(vdev), true, "realized", errp);
+}
+
+static void virtio_nvme_pci_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass);
+    PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass);
+
+    set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
+    k->realize = virtio_nvme_pci_realize;
+    pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET;
+    pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_NVME;
+    pcidev_k->revision = VIRTIO_PCI_ABI_VERSION;
+    pcidev_k->class_id = PCI_CLASS_STORAGE_EXPRESS;
+}
+
+static const TypeInfo virtio_nvme_pci_info = {
+    .name          = TYPE_VIRTIO_NVME_PCI,
+    .parent        = TYPE_VIRTIO_PCI,
+    .instance_size = sizeof(VirtIONVMEPCI),
+    .instance_init = virtio_nvme_pci_instance_init,
+    .class_init    = virtio_nvme_pci_class_init,
+};
+
 /* virtio-scsi-pci */
 
 static Property virtio_scsi_pci_properties[] = {
@@ -2233,6 +2274,7 @@ static void virtio_pci_register_types(void)
 #ifdef CONFIG_VHOST_SCSI
     type_register_static(&vhost_scsi_pci_info);
 #endif
+    type_register_static(&virtio_nvme_pci_info);
 }
 
 type_init(virtio_pci_register_types)
diff --git a/hw/virtio/virtio-pci.h b/hw/virtio/virtio-pci.h
index b6c442f..ff681a6 100644
--- a/hw/virtio/virtio-pci.h
+++ b/hw/virtio/virtio-pci.h
@@ -32,10 +32,12 @@
 #ifdef CONFIG_VHOST_SCSI
 #include "hw/virtio/vhost-scsi.h"
 #endif
+#include "hw/virtio/virtio-nvme.h"
 
 typedef struct VirtIOPCIProxy VirtIOPCIProxy;
 typedef struct VirtIOBlkPCI VirtIOBlkPCI;
 typedef struct VirtIOSCSIPCI VirtIOSCSIPCI;
+typedef struct VirtIONVMEPCI VirtIONVMEPCI;
 typedef struct VirtIOBalloonPCI VirtIOBalloonPCI;
 typedef struct VirtIOSerialPCI VirtIOSerialPCI;
 typedef struct VirtIONetPCI VirtIONetPCI;
@@ -179,6 +181,18 @@ struct VirtIOBlkPCI {
 };
 
 /*
+ * virtio-nvme-pci: This extends VirtioPCIProxy.
+ */
+#define TYPE_VIRTIO_NVME_PCI "virtio-nvme-pci"
+#define VIRTIO_NVME_PCI(obj) \
+        OBJECT_CHECK(VirtIONVMEPCI, (obj), TYPE_VIRTIO_NVME_PCI)
+
+struct VirtIONVMEPCI {
+    VirtIOPCIProxy parent_obj;
+    VirtIONVME vdev;
+};
+
+/*
  * virtio-balloon-pci: This extends VirtioPCIProxy.
  */
 #define TYPE_VIRTIO_BALLOON_PCI "virtio-balloon-pci"
diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h
index 551cb3d..3e8d501 100644
--- a/include/hw/pci/pci.h
+++ b/include/hw/pci/pci.h
@@ -81,6 +81,7 @@
 #define PCI_DEVICE_ID_VIRTIO_SCSI        0x1004
 #define PCI_DEVICE_ID_VIRTIO_RNG         0x1005
 #define PCI_DEVICE_ID_VIRTIO_9P          0x1009
+#define PCI_DEVICE_ID_VIRTIO_NVME        0x100a
 
 #define PCI_VENDOR_ID_REDHAT             0x1b36
 #define PCI_DEVICE_ID_REDHAT_BRIDGE      0x0001
diff --git a/include/hw/virtio/virtio-nvme.h b/include/hw/virtio/virtio-nvme.h
new file mode 100644
index 0000000..4cafddb
--- /dev/null
+++ b/include/hw/virtio/virtio-nvme.h
@@ -0,0 +1,60 @@
+#ifndef _QEMU_VIRTIO_NVME_H
+#define _QEMU_VIRTIO_NVME_H
+
+#include "standard-headers/linux/virtio_blk.h"
+#include "hw/virtio/virtio.h"
+#include "hw/block/block.h"
+#include "sysemu/iothread.h"
+#include "sysemu/block-backend.h"
+#include "hw/block/block.h"
+#include "hw/block/nvme.h"
+
+#define TYPE_VIRTIO_NVME "virtio-nvme"
+#define VIRTIO_NVME(obj) \
+        OBJECT_CHECK(VirtIONVME, (obj), TYPE_VIRTIO_NVME)
+
+struct VirtIONVMEConf {
+    BlockConf conf;
+    uint32_t num_queues;
+};
+
+typedef struct VirtIONVME {
+    VirtIODevice parent_obj;
+    BlockBackend *blk;
+    struct VirtIONVMEConf conf;
+
+    NvmeBar      bar;
+    VirtQueue *admin_vq;
+    VirtQueue **io_vqs;
+
+    uint32_t    page_size;
+    uint16_t    page_bits;
+    uint16_t    max_prp_ents;
+    uint16_t    cqe_size;
+    uint16_t    sqe_size;
+    uint32_t    reg_size;
+    uint32_t    num_namespaces;
+    uint32_t    max_q_ents;
+    uint64_t    ns_size;
+
+    char            *serial;
+    NvmeNamespace   *namespaces;
+    NvmeIdCtrl      id_ctrl;
+} VirtIONVME;
+
+struct virtio_nvme_resp {
+    uint32_t    result;
+    uint16_t    cid;
+    uint16_t    status;
+};
+
+typedef struct VirtIONVMEReq {
+    VirtIONVME *dev;
+    VirtQueue *vq;
+    VirtQueueElement elem;
+    struct NvmeCmd cmd;
+    QEMUIOVector qiov;
+    struct virtio_nvme_resp *resp;
+} VirtIONVMEReq;
+
+#endif
diff --git a/include/standard-headers/linux/virtio_ids.h b/include/standard-headers/linux/virtio_ids.h
index 77925f5..d59d323 100644
--- a/include/standard-headers/linux/virtio_ids.h
+++ b/include/standard-headers/linux/virtio_ids.h
@@ -41,5 +41,6 @@
 #define VIRTIO_ID_CAIF	       12 /* Virtio caif */
 #define VIRTIO_ID_GPU          16 /* virtio GPU */
 #define VIRTIO_ID_INPUT        18 /* virtio input */
+#define VIRTIO_ID_NVME         19 /* TBD: virtio NVMe, need Redhat's help to get this id */
 
 #endif /* _LINUX_VIRTIO_IDS_H */
diff --git a/include/standard-headers/linux/virtio_nvme.h b/include/standard-headers/linux/virtio_nvme.h
new file mode 100644
index 0000000..8cc896c
--- /dev/null
+++ b/include/standard-headers/linux/virtio_nvme.h
@@ -0,0 +1,16 @@
+#ifndef _LINUX_VIRTIO_NVME_H
+#define _LINUX_VIRTIO_NVME_H
+
+/* Feature bits */
+#define VIRTIO_NVME_F_SEG_MAX   1       /* Indicates maximum # of segments */
+#define VIRTIO_NVME_F_MQ        2       /* support more than one vq */
+
+struct virtio_nvme_config {
+        uint64_t   cap;
+        uint32_t   ctrl_config;
+        uint32_t   csts;
+        uint32_t   seg_max;
+        uint32_t   num_queues;
+} QEMU_PACKED;
+
+#endif
-- 
1.9.1




More information about the Linux-nvme mailing list