[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