summaryrefslogtreecommitdiffstats
path: root/src/spdk/lib/bdev/virtio/bdev_virtio_blk.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/spdk/lib/bdev/virtio/bdev_virtio_blk.c')
-rw-r--r--src/spdk/lib/bdev/virtio/bdev_virtio_blk.c707
1 files changed, 707 insertions, 0 deletions
diff --git a/src/spdk/lib/bdev/virtio/bdev_virtio_blk.c b/src/spdk/lib/bdev/virtio/bdev_virtio_blk.c
new file mode 100644
index 00000000..598f7f15
--- /dev/null
+++ b/src/spdk/lib/bdev/virtio/bdev_virtio_blk.c
@@ -0,0 +1,707 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright (c) Intel Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "spdk/stdinc.h"
+
+#include "spdk/bdev.h"
+#include "spdk/conf.h"
+#include "spdk/endian.h"
+#include "spdk/env.h"
+#include "spdk/thread.h"
+#include "spdk/string.h"
+#include "spdk/util.h"
+#include "spdk/json.h"
+
+#include "spdk_internal/assert.h"
+#include "spdk/bdev_module.h"
+#include "spdk_internal/log.h"
+#include "spdk_internal/virtio.h"
+
+#include <linux/virtio_blk.h>
+
+#include "bdev_virtio.h"
+
+struct virtio_blk_dev {
+ struct virtio_dev vdev;
+ struct spdk_bdev bdev;
+ bool readonly;
+};
+
+struct virtio_blk_io_ctx {
+ struct iovec iov_req;
+ struct iovec iov_resp;
+ struct virtio_blk_outhdr req;
+ uint8_t resp;
+};
+
+struct bdev_virtio_blk_io_channel {
+ struct virtio_dev *vdev;
+
+ /** Virtqueue exclusively assigned to this channel. */
+ struct virtqueue *vq;
+
+ /** Virtio response poller. */
+ struct spdk_poller *poller;
+};
+
+/* Features desired/implemented by this driver. */
+#define VIRTIO_BLK_DEV_SUPPORTED_FEATURES \
+ (1ULL << VIRTIO_BLK_F_BLK_SIZE | \
+ 1ULL << VIRTIO_BLK_F_TOPOLOGY | \
+ 1ULL << VIRTIO_BLK_F_MQ | \
+ 1ULL << VIRTIO_BLK_F_RO | \
+ 1ULL << VIRTIO_RING_F_EVENT_IDX | \
+ 1ULL << VHOST_USER_F_PROTOCOL_FEATURES)
+
+static int bdev_virtio_initialize(void);
+static int bdev_virtio_blk_get_ctx_size(void);
+
+static struct spdk_bdev_module virtio_blk_if = {
+ .name = "virtio_blk",
+ .module_init = bdev_virtio_initialize,
+ .get_ctx_size = bdev_virtio_blk_get_ctx_size,
+};
+
+SPDK_BDEV_MODULE_REGISTER(&virtio_blk_if)
+
+static int bdev_virtio_blk_ch_create_cb(void *io_device, void *ctx_buf);
+static void bdev_virtio_blk_ch_destroy_cb(void *io_device, void *ctx_buf);
+
+static struct virtio_blk_io_ctx *
+bdev_virtio_blk_init_io_vreq(struct spdk_io_channel *ch, struct spdk_bdev_io *bdev_io)
+{
+ struct virtio_blk_outhdr *req;
+ uint8_t *resp;
+ struct virtio_blk_io_ctx *io_ctx = (struct virtio_blk_io_ctx *)bdev_io->driver_ctx;
+
+ req = &io_ctx->req;
+ resp = &io_ctx->resp;
+
+ io_ctx->iov_req.iov_base = req;
+ io_ctx->iov_req.iov_len = sizeof(*req);
+
+ io_ctx->iov_resp.iov_base = resp;
+ io_ctx->iov_resp.iov_len = sizeof(*resp);
+
+ memset(req, 0, sizeof(*req));
+ return io_ctx;
+}
+
+static void
+bdev_virtio_blk_send_io(struct spdk_io_channel *ch, struct spdk_bdev_io *bdev_io)
+{
+ struct bdev_virtio_blk_io_channel *virtio_channel = spdk_io_channel_get_ctx(ch);
+ struct virtqueue *vq = virtio_channel->vq;
+ struct virtio_blk_io_ctx *io_ctx = (struct virtio_blk_io_ctx *)bdev_io->driver_ctx;
+ int rc;
+
+ rc = virtqueue_req_start(vq, bdev_io, bdev_io->u.bdev.iovcnt + 2);
+ if (rc == -ENOMEM) {
+ spdk_bdev_io_complete(bdev_io, SPDK_BDEV_IO_STATUS_NOMEM);
+ return;
+ } else if (rc != 0) {
+ spdk_bdev_io_complete(bdev_io, SPDK_BDEV_IO_STATUS_FAILED);
+ return;
+ }
+
+ virtqueue_req_add_iovs(vq, &io_ctx->iov_req, 1, SPDK_VIRTIO_DESC_RO);
+ virtqueue_req_add_iovs(vq, bdev_io->u.bdev.iovs, bdev_io->u.bdev.iovcnt,
+ bdev_io->type == SPDK_BDEV_IO_TYPE_READ ?
+ SPDK_VIRTIO_DESC_WR : SPDK_VIRTIO_DESC_RO);
+ virtqueue_req_add_iovs(vq, &io_ctx->iov_resp, 1, SPDK_VIRTIO_DESC_WR);
+
+ virtqueue_req_flush(vq);
+}
+
+static void
+bdev_virtio_rw(struct spdk_io_channel *ch, struct spdk_bdev_io *bdev_io)
+{
+ struct virtio_blk_io_ctx *io_ctx = bdev_virtio_blk_init_io_vreq(ch, bdev_io);
+ struct virtio_blk_outhdr *req = &io_ctx->req;
+
+ req->type = bdev_io->type == SPDK_BDEV_IO_TYPE_WRITE ?
+ VIRTIO_BLK_T_OUT : VIRTIO_BLK_T_IN;
+
+ req->sector = bdev_io->u.bdev.offset_blocks *
+ spdk_bdev_get_block_size(bdev_io->bdev) / 512;
+
+ bdev_virtio_blk_send_io(ch, bdev_io);
+}
+
+static int
+_bdev_virtio_submit_request(struct spdk_io_channel *ch, struct spdk_bdev_io *bdev_io)
+{
+ struct virtio_blk_dev *bvdev = bdev_io->bdev->ctxt;
+
+ switch (bdev_io->type) {
+ case SPDK_BDEV_IO_TYPE_READ:
+ spdk_bdev_io_get_buf(bdev_io, bdev_virtio_rw,
+ bdev_io->u.bdev.num_blocks * bdev_io->bdev->blocklen);
+ return 0;
+ case SPDK_BDEV_IO_TYPE_WRITE:
+ if (bvdev->readonly) {
+ spdk_bdev_io_complete(bdev_io, SPDK_BDEV_IO_STATUS_FAILED);
+ } else {
+ bdev_virtio_rw(ch, bdev_io);
+ }
+ return 0;
+ case SPDK_BDEV_IO_TYPE_RESET:
+ spdk_bdev_io_complete(bdev_io, SPDK_BDEV_IO_STATUS_SUCCESS);
+ return 0;
+ case SPDK_BDEV_IO_TYPE_UNMAP:
+ case SPDK_BDEV_IO_TYPE_FLUSH:
+ default:
+ return -1;
+ }
+
+ SPDK_UNREACHABLE();
+}
+
+static void
+bdev_virtio_submit_request(struct spdk_io_channel *ch, struct spdk_bdev_io *bdev_io)
+{
+ if (_bdev_virtio_submit_request(ch, bdev_io) < 0) {
+ spdk_bdev_io_complete(bdev_io, SPDK_BDEV_IO_STATUS_FAILED);
+ }
+}
+
+static bool
+bdev_virtio_io_type_supported(void *ctx, enum spdk_bdev_io_type io_type)
+{
+ struct virtio_blk_dev *bvdev = ctx;
+
+ switch (io_type) {
+ case SPDK_BDEV_IO_TYPE_READ:
+ case SPDK_BDEV_IO_TYPE_RESET:
+ return true;
+ case SPDK_BDEV_IO_TYPE_WRITE:
+ return !bvdev->readonly;
+ case SPDK_BDEV_IO_TYPE_FLUSH:
+ case SPDK_BDEV_IO_TYPE_UNMAP:
+ default:
+ return false;
+ }
+}
+
+static struct spdk_io_channel *
+bdev_virtio_get_io_channel(void *ctx)
+{
+ struct virtio_blk_dev *bvdev = ctx;
+
+ return spdk_get_io_channel(bvdev);
+}
+
+static void
+virtio_blk_dev_unregister_cb(void *io_device)
+{
+ struct virtio_blk_dev *bvdev = io_device;
+ struct virtio_dev *vdev = &bvdev->vdev;
+
+ virtio_dev_stop(vdev);
+ virtio_dev_destruct(vdev);
+ spdk_bdev_destruct_done(&bvdev->bdev, 0);
+ free(bvdev);
+}
+
+static int
+bdev_virtio_disk_destruct(void *ctx)
+{
+ struct virtio_blk_dev *bvdev = ctx;
+
+ spdk_io_device_unregister(bvdev, virtio_blk_dev_unregister_cb);
+ return 1;
+}
+
+int
+bdev_virtio_blk_dev_remove(const char *name, bdev_virtio_remove_cb cb_fn, void *cb_arg)
+{
+ struct spdk_bdev *bdev;
+
+ bdev = spdk_bdev_get_by_name(name);
+ if (bdev == NULL) {
+ return -ENODEV;
+ }
+
+ if (bdev->module != &virtio_blk_if) {
+ return -ENODEV;
+ }
+
+ spdk_bdev_unregister(bdev, cb_fn, cb_arg);
+
+ return 0;
+}
+
+static int
+bdev_virtio_dump_json_config(void *ctx, struct spdk_json_write_ctx *w)
+{
+ struct virtio_blk_dev *bvdev = ctx;
+
+ virtio_dev_dump_json_info(&bvdev->vdev, w);
+ return 0;
+}
+
+static void
+bdev_virtio_write_config_json(struct spdk_bdev *bdev, struct spdk_json_write_ctx *w)
+{
+ struct virtio_blk_dev *bvdev = bdev->ctxt;
+
+ spdk_json_write_object_begin(w);
+
+ spdk_json_write_named_string(w, "method", "construct_virtio_dev");
+
+ spdk_json_write_named_object_begin(w, "params");
+ spdk_json_write_named_string(w, "name", bvdev->vdev.name);
+ spdk_json_write_named_string(w, "dev_type", "blk");
+
+ /* Write transport specific parameters. */
+ bvdev->vdev.backend_ops->write_json_config(&bvdev->vdev, w);
+
+ spdk_json_write_object_end(w);
+
+ spdk_json_write_object_end(w);
+}
+
+static const struct spdk_bdev_fn_table virtio_fn_table = {
+ .destruct = bdev_virtio_disk_destruct,
+ .submit_request = bdev_virtio_submit_request,
+ .io_type_supported = bdev_virtio_io_type_supported,
+ .get_io_channel = bdev_virtio_get_io_channel,
+ .dump_info_json = bdev_virtio_dump_json_config,
+ .write_config_json = bdev_virtio_write_config_json,
+};
+
+static void
+bdev_virtio_io_cpl(struct spdk_bdev_io *bdev_io)
+{
+ struct virtio_blk_io_ctx *io_ctx = (struct virtio_blk_io_ctx *)bdev_io->driver_ctx;
+
+ spdk_bdev_io_complete(bdev_io, io_ctx->resp == VIRTIO_BLK_S_OK ?
+ SPDK_BDEV_IO_STATUS_SUCCESS : SPDK_BDEV_IO_STATUS_FAILED);
+}
+
+static int
+bdev_virtio_poll(void *arg)
+{
+ struct bdev_virtio_blk_io_channel *ch = arg;
+ void *io[32];
+ uint32_t io_len[32];
+ uint16_t i, cnt;
+
+ cnt = virtio_recv_pkts(ch->vq, io, io_len, SPDK_COUNTOF(io));
+ for (i = 0; i < cnt; ++i) {
+ bdev_virtio_io_cpl(io[i]);
+ }
+
+ return cnt;
+}
+
+static int
+bdev_virtio_blk_ch_create_cb(void *io_device, void *ctx_buf)
+{
+ struct virtio_blk_dev *bvdev = io_device;
+ struct virtio_dev *vdev = &bvdev->vdev;
+ struct bdev_virtio_blk_io_channel *ch = ctx_buf;
+ struct virtqueue *vq;
+ int32_t queue_idx;
+
+ queue_idx = virtio_dev_find_and_acquire_queue(vdev, 0);
+ if (queue_idx < 0) {
+ SPDK_ERRLOG("Couldn't get an unused queue for the io_channel.\n");
+ return -1;
+ }
+
+ vq = vdev->vqs[queue_idx];
+
+ ch->vdev = vdev;
+ ch->vq = vq;
+
+ ch->poller = spdk_poller_register(bdev_virtio_poll, ch, 0);
+ return 0;
+}
+
+static void
+bdev_virtio_blk_ch_destroy_cb(void *io_device, void *ctx_buf)
+{
+ struct virtio_blk_dev *bvdev = io_device;
+ struct virtio_dev *vdev = &bvdev->vdev;
+ struct bdev_virtio_blk_io_channel *ch = ctx_buf;
+ struct virtqueue *vq = ch->vq;
+
+ spdk_poller_unregister(&ch->poller);
+ virtio_dev_release_queue(vdev, vq->vq_queue_index);
+}
+
+static int
+virtio_blk_dev_init(struct virtio_blk_dev *bvdev, uint16_t max_queues)
+{
+ struct virtio_dev *vdev = &bvdev->vdev;
+ struct spdk_bdev *bdev = &bvdev->bdev;
+ uint64_t capacity, num_blocks;
+ uint32_t block_size;
+ uint16_t host_max_queues;
+ int rc;
+
+ if (virtio_dev_has_feature(vdev, VIRTIO_BLK_F_BLK_SIZE)) {
+ rc = virtio_dev_read_dev_config(vdev, offsetof(struct virtio_blk_config, blk_size),
+ &block_size, sizeof(block_size));
+ if (rc) {
+ SPDK_ERRLOG("%s: config read failed: %s\n", vdev->name, spdk_strerror(-rc));
+ return rc;
+ }
+
+ if (block_size == 0 || block_size % 512 != 0) {
+ SPDK_ERRLOG("%s: invalid block size (%"PRIu32"). Must be "
+ "a multiple of 512.\n", vdev->name, block_size);
+ return -EIO;
+ }
+ } else {
+ block_size = 512;
+ }
+
+ rc = virtio_dev_read_dev_config(vdev, offsetof(struct virtio_blk_config, capacity),
+ &capacity, sizeof(capacity));
+ if (rc) {
+ SPDK_ERRLOG("%s: config read failed: %s\n", vdev->name, spdk_strerror(-rc));
+ return rc;
+ }
+
+ /* `capacity` is a number of 512-byte sectors. */
+ num_blocks = capacity * 512 / block_size;
+ if (num_blocks == 0) {
+ SPDK_ERRLOG("%s: size too small (size: %"PRIu64", blocksize: %"PRIu32").\n",
+ vdev->name, capacity * 512, block_size);
+ return -EIO;
+ }
+
+ if ((capacity * 512) % block_size != 0) {
+ SPDK_WARNLOG("%s: size has been rounded down to the nearest block size boundary. "
+ "(block size: %"PRIu32", previous size: %"PRIu64", new size: %"PRIu64")\n",
+ vdev->name, block_size, capacity * 512, num_blocks * block_size);
+ }
+
+ if (virtio_dev_has_feature(vdev, VIRTIO_BLK_F_MQ)) {
+ rc = virtio_dev_read_dev_config(vdev, offsetof(struct virtio_blk_config, num_queues),
+ &host_max_queues, sizeof(host_max_queues));
+ if (rc) {
+ SPDK_ERRLOG("%s: config read failed: %s\n", vdev->name, spdk_strerror(-rc));
+ return rc;
+ }
+ } else {
+ host_max_queues = 1;
+ }
+
+ if (virtio_dev_has_feature(vdev, VIRTIO_BLK_F_RO)) {
+ bvdev->readonly = true;
+ }
+
+ if (max_queues == 0) {
+ SPDK_ERRLOG("%s: requested 0 request queues (%"PRIu16" available).\n",
+ vdev->name, host_max_queues);
+ return -EINVAL;
+ }
+
+ if (max_queues > host_max_queues) {
+ SPDK_WARNLOG("%s: requested %"PRIu16" request queues "
+ "but only %"PRIu16" available.\n",
+ vdev->name, max_queues, host_max_queues);
+ max_queues = host_max_queues;
+ }
+
+ /* bdev is tied with the virtio device; we can reuse the name */
+ bdev->name = vdev->name;
+ rc = virtio_dev_start(vdev, max_queues, 0);
+ if (rc != 0) {
+ return rc;
+ }
+
+ bdev->product_name = "VirtioBlk Disk";
+ bdev->write_cache = 0;
+ bdev->blocklen = block_size;
+ bdev->blockcnt = num_blocks;
+
+ bdev->ctxt = bvdev;
+ bdev->fn_table = &virtio_fn_table;
+ bdev->module = &virtio_blk_if;
+
+ spdk_io_device_register(bvdev, bdev_virtio_blk_ch_create_cb,
+ bdev_virtio_blk_ch_destroy_cb,
+ sizeof(struct bdev_virtio_blk_io_channel),
+ vdev->name);
+
+ rc = spdk_bdev_register(bdev);
+ if (rc) {
+ SPDK_ERRLOG("Failed to register bdev name=%s\n", bdev->name);
+ spdk_io_device_unregister(bvdev, NULL);
+ virtio_dev_stop(vdev);
+ return rc;
+ }
+
+ return 0;
+}
+
+static struct virtio_blk_dev *
+virtio_pci_blk_dev_create(const char *name, struct virtio_pci_ctx *pci_ctx)
+{
+ static int pci_dev_counter = 0;
+ struct virtio_blk_dev *bvdev;
+ struct virtio_dev *vdev;
+ char *default_name = NULL;
+ uint16_t num_queues;
+ int rc;
+
+ bvdev = calloc(1, sizeof(*bvdev));
+ if (bvdev == NULL) {
+ SPDK_ERRLOG("virtio device calloc failed\n");
+ return NULL;
+ }
+ vdev = &bvdev->vdev;
+
+ if (name == NULL) {
+ default_name = spdk_sprintf_alloc("VirtioBlk%"PRIu32, pci_dev_counter++);
+ if (default_name == NULL) {
+ free(vdev);
+ return NULL;
+ }
+ name = default_name;
+ }
+
+ rc = virtio_pci_dev_init(vdev, name, pci_ctx);
+ free(default_name);
+
+ if (rc != 0) {
+ free(bvdev);
+ return NULL;
+ }
+
+ rc = virtio_dev_reset(vdev, VIRTIO_BLK_DEV_SUPPORTED_FEATURES);
+ if (rc != 0) {
+ virtio_dev_destruct(vdev);
+ free(bvdev);
+ return NULL;
+ }
+
+ /* TODO: add a way to limit usable virtqueues */
+ if (virtio_dev_has_feature(vdev, VIRTIO_BLK_F_MQ)) {
+ rc = virtio_dev_read_dev_config(vdev, offsetof(struct virtio_blk_config, num_queues),
+ &num_queues, sizeof(num_queues));
+ if (rc) {
+ SPDK_ERRLOG("%s: config read failed: %s\n", vdev->name, spdk_strerror(-rc));
+ virtio_dev_destruct(vdev);
+ free(bvdev);
+ return NULL;
+ }
+ } else {
+ num_queues = 1;
+ }
+
+ rc = virtio_blk_dev_init(bvdev, num_queues);
+ if (rc != 0) {
+ virtio_dev_destruct(vdev);
+ free(bvdev);
+ return NULL;
+ }
+
+ return bvdev;
+}
+
+static struct virtio_blk_dev *
+virtio_user_blk_dev_create(const char *name, const char *path,
+ uint16_t num_queues, uint32_t queue_size)
+{
+ struct virtio_blk_dev *bvdev;
+ int rc;
+
+ bvdev = calloc(1, sizeof(*bvdev));
+ if (bvdev == NULL) {
+ SPDK_ERRLOG("calloc failed for virtio device %s: %s\n", name, path);
+ return NULL;
+ }
+
+ rc = virtio_user_dev_init(&bvdev->vdev, name, path, queue_size);
+ if (rc != 0) {
+ SPDK_ERRLOG("Failed to create virito device %s: %s\n", name, path);
+ free(bvdev);
+ return NULL;
+ }
+
+ rc = virtio_dev_reset(&bvdev->vdev, VIRTIO_BLK_DEV_SUPPORTED_FEATURES);
+ if (rc != 0) {
+ virtio_dev_destruct(&bvdev->vdev);
+ free(bvdev);
+ return NULL;
+ }
+
+ rc = virtio_blk_dev_init(bvdev, num_queues);
+ if (rc != 0) {
+ virtio_dev_destruct(&bvdev->vdev);
+ free(bvdev);
+ return NULL;
+ }
+
+ return bvdev;
+}
+
+struct bdev_virtio_pci_dev_create_ctx {
+ const char *name;
+ struct virtio_blk_dev *ret;
+};
+
+static int
+bdev_virtio_pci_blk_dev_create_cb(struct virtio_pci_ctx *pci_ctx, void *ctx)
+{
+ struct bdev_virtio_pci_dev_create_ctx *create_ctx = ctx;
+
+ create_ctx->ret = virtio_pci_blk_dev_create(create_ctx->name, pci_ctx);
+ if (create_ctx->ret == NULL) {
+ return -1;
+ }
+
+ return 0;
+}
+
+struct spdk_bdev *
+bdev_virtio_pci_blk_dev_create(const char *name, struct spdk_pci_addr *pci_addr)
+{
+ struct bdev_virtio_pci_dev_create_ctx create_ctx;
+
+ create_ctx.name = name;
+ create_ctx.ret = NULL;
+
+ virtio_pci_dev_attach(bdev_virtio_pci_blk_dev_create_cb, &create_ctx,
+ PCI_DEVICE_ID_VIRTIO_BLK_MODERN, pci_addr);
+
+ if (create_ctx.ret == NULL) {
+ return NULL;
+ }
+
+ return &create_ctx.ret->bdev;
+}
+
+static int
+virtio_pci_blk_dev_enumerate_cb(struct virtio_pci_ctx *pci_ctx, void *ctx)
+{
+ struct virtio_blk_dev *bvdev;
+
+ bvdev = virtio_pci_blk_dev_create(NULL, pci_ctx);
+ return bvdev == NULL ? -1 : 0;
+}
+
+static int
+bdev_virtio_initialize(void)
+{
+ struct spdk_conf_section *sp;
+ struct virtio_blk_dev *bvdev;
+ char *default_name = NULL;
+ char *path, *type, *name;
+ unsigned vdev_num;
+ int num_queues;
+ bool enable_pci;
+ int rc = 0;
+
+ for (sp = spdk_conf_first_section(NULL); sp != NULL; sp = spdk_conf_next_section(sp)) {
+ if (!spdk_conf_section_match_prefix(sp, "VirtioUser")) {
+ continue;
+ }
+
+ if (sscanf(spdk_conf_section_get_name(sp), "VirtioUser%u", &vdev_num) != 1) {
+ SPDK_ERRLOG("Section '%s' has non-numeric suffix.\n",
+ spdk_conf_section_get_name(sp));
+ return -1;
+ }
+
+ path = spdk_conf_section_get_val(sp, "Path");
+ if (path == NULL) {
+ SPDK_ERRLOG("VirtioUserBlk%u: missing Path\n", vdev_num);
+ return -1;
+ }
+
+ type = spdk_conf_section_get_val(sp, "Type");
+ if (type == NULL || strcmp(type, "Blk") != 0) {
+ continue;
+ }
+
+ num_queues = spdk_conf_section_get_intval(sp, "Queues");
+ if (num_queues < 1) {
+ num_queues = 1;
+ }
+
+ name = spdk_conf_section_get_val(sp, "Name");
+ if (name == NULL) {
+ default_name = spdk_sprintf_alloc("VirtioBlk%u", vdev_num);
+ name = default_name;
+ }
+
+ bvdev = virtio_user_blk_dev_create(name, path, num_queues, 512);
+ free(default_name);
+ default_name = NULL;
+
+ if (bvdev == NULL) {
+ return -1;
+ }
+ }
+
+ sp = spdk_conf_find_section(NULL, "VirtioPci");
+ if (sp == NULL) {
+ return 0;
+ }
+
+ enable_pci = spdk_conf_section_get_boolval(sp, "Enable", false);
+ if (enable_pci) {
+ rc = virtio_pci_dev_enumerate(virtio_pci_blk_dev_enumerate_cb, NULL,
+ PCI_DEVICE_ID_VIRTIO_BLK_MODERN);
+ }
+
+ return rc;
+}
+
+struct spdk_bdev *
+bdev_virtio_user_blk_dev_create(const char *name, const char *path,
+ unsigned num_queues, unsigned queue_size)
+{
+ struct virtio_blk_dev *bvdev;
+
+ bvdev = virtio_user_blk_dev_create(name, path, num_queues, queue_size);
+ if (bvdev == NULL) {
+ return NULL;
+ }
+
+ return &bvdev->bdev;
+}
+
+static int
+bdev_virtio_blk_get_ctx_size(void)
+{
+ return sizeof(struct virtio_blk_io_ctx);
+}
+
+SPDK_LOG_REGISTER_COMPONENT("virtio_blk", SPDK_LOG_VIRTIO_BLK)