xref: /spdk/module/bdev/virtio/bdev_virtio_blk.c (revision a6dbe3721eb3b5990707fc3e378c95e505dd8ab5)
1488570ebSJim Harris /*   SPDX-License-Identifier: BSD-3-Clause
2*a6dbe372Spaul luse  *   Copyright (C) 2017 Intel Corporation.
307fe6a43SSeth Howell  *   All rights reserved.
407fe6a43SSeth Howell  */
507fe6a43SSeth Howell 
607fe6a43SSeth Howell #include "spdk/stdinc.h"
707fe6a43SSeth Howell 
807fe6a43SSeth Howell #include "spdk/bdev.h"
907fe6a43SSeth Howell #include "spdk/endian.h"
1007fe6a43SSeth Howell #include "spdk/env.h"
1107fe6a43SSeth Howell #include "spdk/thread.h"
1207fe6a43SSeth Howell #include "spdk/string.h"
1307fe6a43SSeth Howell #include "spdk/util.h"
1407fe6a43SSeth Howell #include "spdk/json.h"
1507fe6a43SSeth Howell 
1607fe6a43SSeth Howell #include "spdk_internal/assert.h"
1707fe6a43SSeth Howell #include "spdk/bdev_module.h"
184e8e97c8STomasz Zawadzki #include "spdk/log.h"
1907fe6a43SSeth Howell #include "spdk_internal/virtio.h"
20e7379fc2SChangpeng Liu #include "spdk_internal/vhost_user.h"
2107fe6a43SSeth Howell 
2207fe6a43SSeth Howell #include <linux/virtio_blk.h>
234c890c31SJin Yu #include <linux/virtio_ids.h>
2407fe6a43SSeth Howell 
2507fe6a43SSeth Howell #include "bdev_virtio.h"
2607fe6a43SSeth Howell 
2707fe6a43SSeth Howell struct virtio_blk_dev {
2807fe6a43SSeth Howell 	struct virtio_dev		vdev;
2907fe6a43SSeth Howell 	struct spdk_bdev		bdev;
3007fe6a43SSeth Howell 	bool				readonly;
3107fe6a43SSeth Howell 	bool				unmap;
3207fe6a43SSeth Howell };
3307fe6a43SSeth Howell 
3407fe6a43SSeth Howell struct virtio_blk_io_ctx {
3507fe6a43SSeth Howell 	struct iovec				iov_req;
3607fe6a43SSeth Howell 	struct iovec				iov_resp;
3707fe6a43SSeth Howell 	struct iovec				iov_unmap;
3807fe6a43SSeth Howell 	struct virtio_blk_outhdr		req;
3907fe6a43SSeth Howell 	struct virtio_blk_discard_write_zeroes	unmap;
4007fe6a43SSeth Howell 	uint8_t					resp;
4107fe6a43SSeth Howell };
4207fe6a43SSeth Howell 
4307fe6a43SSeth Howell struct bdev_virtio_blk_io_channel {
4407fe6a43SSeth Howell 	struct virtio_dev		*vdev;
4507fe6a43SSeth Howell 
4607fe6a43SSeth Howell 	/** Virtqueue exclusively assigned to this channel. */
4707fe6a43SSeth Howell 	struct virtqueue		*vq;
4807fe6a43SSeth Howell 
4907fe6a43SSeth Howell 	/** Virtio response poller. */
5007fe6a43SSeth Howell 	struct spdk_poller		*poller;
5107fe6a43SSeth Howell };
5207fe6a43SSeth Howell 
5307fe6a43SSeth Howell /* Features desired/implemented by this driver. */
5407fe6a43SSeth Howell #define VIRTIO_BLK_DEV_SUPPORTED_FEATURES		\
55b924daecSJin Yu 	(1ULL << VIRTIO_BLK_F_SIZE_MAX		|	\
56b924daecSJin Yu 	 1ULL << VIRTIO_BLK_F_SEG_MAX		|	\
57b924daecSJin Yu 	 1ULL << VIRTIO_BLK_F_BLK_SIZE		|	\
5807fe6a43SSeth Howell 	 1ULL << VIRTIO_BLK_F_TOPOLOGY		|	\
5907fe6a43SSeth Howell 	 1ULL << VIRTIO_BLK_F_MQ		|	\
6007fe6a43SSeth Howell 	 1ULL << VIRTIO_BLK_F_RO		|	\
6107fe6a43SSeth Howell 	 1ULL << VIRTIO_BLK_F_DISCARD		|	\
62515d028eSChangpeng Liu 	 1ULL << VIRTIO_RING_F_EVENT_IDX)
6307fe6a43SSeth Howell 
64ebea4dd6SJin Yu /* 10 sec for max poll period */
65ebea4dd6SJin Yu #define VIRTIO_BLK_HOTPLUG_POLL_PERIOD_MAX		10000000ULL
66ebea4dd6SJin Yu /* Default poll period is 100ms */
67ebea4dd6SJin Yu #define VIRTIO_BLK_HOTPLUG_POLL_PERIOD_DEFAULT		100000ULL
68ebea4dd6SJin Yu 
69ebea4dd6SJin Yu static struct spdk_poller *g_blk_hotplug_poller = NULL;
70ebea4dd6SJin Yu static int g_blk_hotplug_fd = -1;
71ebea4dd6SJin Yu 
7207fe6a43SSeth Howell static int bdev_virtio_initialize(void);
7307fe6a43SSeth Howell static int bdev_virtio_blk_get_ctx_size(void);
7407fe6a43SSeth Howell 
7507fe6a43SSeth Howell static struct spdk_bdev_module virtio_blk_if = {
7607fe6a43SSeth Howell 	.name = "virtio_blk",
7707fe6a43SSeth Howell 	.module_init = bdev_virtio_initialize,
7807fe6a43SSeth Howell 	.get_ctx_size = bdev_virtio_blk_get_ctx_size,
7907fe6a43SSeth Howell };
8007fe6a43SSeth Howell 
8107fe6a43SSeth Howell SPDK_BDEV_MODULE_REGISTER(virtio_blk, &virtio_blk_if)
8207fe6a43SSeth Howell 
8307fe6a43SSeth Howell static int bdev_virtio_blk_ch_create_cb(void *io_device, void *ctx_buf);
8407fe6a43SSeth Howell static void bdev_virtio_blk_ch_destroy_cb(void *io_device, void *ctx_buf);
8507fe6a43SSeth Howell 
8607fe6a43SSeth Howell static struct virtio_blk_io_ctx *
bdev_virtio_blk_init_io_vreq(struct spdk_io_channel * ch,struct spdk_bdev_io * bdev_io)8707fe6a43SSeth Howell bdev_virtio_blk_init_io_vreq(struct spdk_io_channel *ch, struct spdk_bdev_io *bdev_io)
8807fe6a43SSeth Howell {
8907fe6a43SSeth Howell 	struct virtio_blk_outhdr *req;
9007fe6a43SSeth Howell 	uint8_t *resp;
9107fe6a43SSeth Howell 	struct virtio_blk_discard_write_zeroes *desc;
9207fe6a43SSeth Howell 
9307fe6a43SSeth Howell 	struct virtio_blk_io_ctx *io_ctx = (struct virtio_blk_io_ctx *)bdev_io->driver_ctx;
9407fe6a43SSeth Howell 
9507fe6a43SSeth Howell 	req = &io_ctx->req;
9607fe6a43SSeth Howell 	resp = &io_ctx->resp;
9707fe6a43SSeth Howell 	desc = &io_ctx->unmap;
9807fe6a43SSeth Howell 
9907fe6a43SSeth Howell 	io_ctx->iov_req.iov_base = req;
10007fe6a43SSeth Howell 	io_ctx->iov_req.iov_len = sizeof(*req);
10107fe6a43SSeth Howell 
10207fe6a43SSeth Howell 	io_ctx->iov_resp.iov_base = resp;
10307fe6a43SSeth Howell 	io_ctx->iov_resp.iov_len = sizeof(*resp);
10407fe6a43SSeth Howell 
10507fe6a43SSeth Howell 	io_ctx->iov_unmap.iov_base = desc;
10607fe6a43SSeth Howell 	io_ctx->iov_unmap.iov_len = sizeof(*desc);
10707fe6a43SSeth Howell 
10807fe6a43SSeth Howell 	memset(req, 0, sizeof(*req));
10907fe6a43SSeth Howell 	return io_ctx;
11007fe6a43SSeth Howell }
11107fe6a43SSeth Howell 
11207fe6a43SSeth Howell static void
bdev_virtio_blk_send_io(struct spdk_io_channel * ch,struct spdk_bdev_io * bdev_io)11307fe6a43SSeth Howell bdev_virtio_blk_send_io(struct spdk_io_channel *ch, struct spdk_bdev_io *bdev_io)
11407fe6a43SSeth Howell {
11507fe6a43SSeth Howell 	struct bdev_virtio_blk_io_channel *virtio_channel = spdk_io_channel_get_ctx(ch);
11607fe6a43SSeth Howell 	struct virtqueue *vq = virtio_channel->vq;
11707fe6a43SSeth Howell 	struct virtio_blk_io_ctx *io_ctx = (struct virtio_blk_io_ctx *)bdev_io->driver_ctx;
11807fe6a43SSeth Howell 	int rc;
11907fe6a43SSeth Howell 
12007fe6a43SSeth Howell 	rc = virtqueue_req_start(vq, bdev_io, bdev_io->u.bdev.iovcnt + 2);
12107fe6a43SSeth Howell 	if (rc == -ENOMEM) {
12207fe6a43SSeth Howell 		spdk_bdev_io_complete(bdev_io, SPDK_BDEV_IO_STATUS_NOMEM);
12307fe6a43SSeth Howell 		return;
12407fe6a43SSeth Howell 	} else if (rc != 0) {
12507fe6a43SSeth Howell 		spdk_bdev_io_complete(bdev_io, SPDK_BDEV_IO_STATUS_FAILED);
12607fe6a43SSeth Howell 		return;
12707fe6a43SSeth Howell 	}
12807fe6a43SSeth Howell 
12907fe6a43SSeth Howell 	virtqueue_req_add_iovs(vq, &io_ctx->iov_req, 1, SPDK_VIRTIO_DESC_RO);
13007fe6a43SSeth Howell 	if (bdev_io->type == SPDK_BDEV_IO_TYPE_UNMAP) {
13107fe6a43SSeth Howell 		virtqueue_req_add_iovs(vq, &io_ctx->iov_unmap, 1, SPDK_VIRTIO_DESC_RO);
13207fe6a43SSeth Howell 	} else {
13307fe6a43SSeth Howell 		virtqueue_req_add_iovs(vq, bdev_io->u.bdev.iovs, bdev_io->u.bdev.iovcnt,
13407fe6a43SSeth Howell 				       bdev_io->type == SPDK_BDEV_IO_TYPE_READ ?
13507fe6a43SSeth Howell 				       SPDK_VIRTIO_DESC_WR : SPDK_VIRTIO_DESC_RO);
13607fe6a43SSeth Howell 	}
13707fe6a43SSeth Howell 	virtqueue_req_add_iovs(vq, &io_ctx->iov_resp, 1, SPDK_VIRTIO_DESC_WR);
13807fe6a43SSeth Howell 
13907fe6a43SSeth Howell 	virtqueue_req_flush(vq);
14007fe6a43SSeth Howell }
14107fe6a43SSeth Howell 
14207fe6a43SSeth Howell static void
bdev_virtio_command(struct spdk_io_channel * ch,struct spdk_bdev_io * bdev_io)14307fe6a43SSeth Howell bdev_virtio_command(struct spdk_io_channel *ch, struct spdk_bdev_io *bdev_io)
14407fe6a43SSeth Howell {
14507fe6a43SSeth Howell 	struct virtio_blk_io_ctx *io_ctx = bdev_virtio_blk_init_io_vreq(ch, bdev_io);
14607fe6a43SSeth Howell 	struct virtio_blk_outhdr *req = &io_ctx->req;
14707fe6a43SSeth Howell 	struct virtio_blk_discard_write_zeroes *desc = &io_ctx->unmap;
14807fe6a43SSeth Howell 
14907fe6a43SSeth Howell 	if (bdev_io->type == SPDK_BDEV_IO_TYPE_READ) {
15007fe6a43SSeth Howell 		req->type = VIRTIO_BLK_T_IN;
15107fe6a43SSeth Howell 	} else if (bdev_io->type == SPDK_BDEV_IO_TYPE_WRITE) {
15207fe6a43SSeth Howell 		req->type = VIRTIO_BLK_T_OUT;
15307fe6a43SSeth Howell 	} else if (bdev_io->type == SPDK_BDEV_IO_TYPE_UNMAP) {
15407fe6a43SSeth Howell 		req->type = VIRTIO_BLK_T_DISCARD;
15507fe6a43SSeth Howell 		desc->sector = bdev_io->u.bdev.offset_blocks *
15607fe6a43SSeth Howell 			       spdk_bdev_get_block_size(bdev_io->bdev) / 512;
15707fe6a43SSeth Howell 		desc->num_sectors = bdev_io->u.bdev.num_blocks *
15807fe6a43SSeth Howell 				    spdk_bdev_get_block_size(bdev_io->bdev) / 512;
15907fe6a43SSeth Howell 		desc->flags = 0;
16007fe6a43SSeth Howell 	}
16107fe6a43SSeth Howell 
16207fe6a43SSeth Howell 	req->sector = bdev_io->u.bdev.offset_blocks *
16307fe6a43SSeth Howell 		      spdk_bdev_get_block_size(bdev_io->bdev) / 512;
16407fe6a43SSeth Howell 
16507fe6a43SSeth Howell 	bdev_virtio_blk_send_io(ch, bdev_io);
16607fe6a43SSeth Howell }
16707fe6a43SSeth Howell 
16807fe6a43SSeth Howell static void
bdev_virtio_get_buf_cb(struct spdk_io_channel * ch,struct spdk_bdev_io * bdev_io,bool success)16907fe6a43SSeth Howell bdev_virtio_get_buf_cb(struct spdk_io_channel *ch, struct spdk_bdev_io *bdev_io,
17007fe6a43SSeth Howell 		       bool success)
17107fe6a43SSeth Howell {
17207fe6a43SSeth Howell 	if (!success) {
17307fe6a43SSeth Howell 		spdk_bdev_io_complete(bdev_io, SPDK_BDEV_IO_STATUS_FAILED);
17407fe6a43SSeth Howell 		return;
17507fe6a43SSeth Howell 	}
17607fe6a43SSeth Howell 
17707fe6a43SSeth Howell 	bdev_virtio_command(ch, bdev_io);
17807fe6a43SSeth Howell }
17907fe6a43SSeth Howell 
18007fe6a43SSeth Howell static int
_bdev_virtio_submit_request(struct spdk_io_channel * ch,struct spdk_bdev_io * bdev_io)18107fe6a43SSeth Howell _bdev_virtio_submit_request(struct spdk_io_channel *ch, struct spdk_bdev_io *bdev_io)
18207fe6a43SSeth Howell {
18307fe6a43SSeth Howell 	struct virtio_blk_dev *bvdev = bdev_io->bdev->ctxt;
18407fe6a43SSeth Howell 
18507fe6a43SSeth Howell 	switch (bdev_io->type) {
18607fe6a43SSeth Howell 	case SPDK_BDEV_IO_TYPE_READ:
18707fe6a43SSeth Howell 		spdk_bdev_io_get_buf(bdev_io, bdev_virtio_get_buf_cb,
18807fe6a43SSeth Howell 				     bdev_io->u.bdev.num_blocks * bdev_io->bdev->blocklen);
18907fe6a43SSeth Howell 		return 0;
19007fe6a43SSeth Howell 	case SPDK_BDEV_IO_TYPE_WRITE:
19107fe6a43SSeth Howell 		if (bvdev->readonly) {
19207fe6a43SSeth Howell 			spdk_bdev_io_complete(bdev_io, SPDK_BDEV_IO_STATUS_FAILED);
19307fe6a43SSeth Howell 		} else {
19407fe6a43SSeth Howell 			bdev_virtio_command(ch, bdev_io);
19507fe6a43SSeth Howell 		}
19607fe6a43SSeth Howell 		return 0;
19707fe6a43SSeth Howell 	case SPDK_BDEV_IO_TYPE_RESET:
19807fe6a43SSeth Howell 		spdk_bdev_io_complete(bdev_io, SPDK_BDEV_IO_STATUS_SUCCESS);
19907fe6a43SSeth Howell 		return 0;
20007fe6a43SSeth Howell 	case SPDK_BDEV_IO_TYPE_UNMAP:
20107fe6a43SSeth Howell 		if (bvdev->unmap) {
20207fe6a43SSeth Howell 			bdev_virtio_command(ch, bdev_io);
20307fe6a43SSeth Howell 		} else {
20407fe6a43SSeth Howell 			spdk_bdev_io_complete(bdev_io, SPDK_BDEV_IO_STATUS_FAILED);
20507fe6a43SSeth Howell 		}
20607fe6a43SSeth Howell 		return 0;
20707fe6a43SSeth Howell 	case SPDK_BDEV_IO_TYPE_FLUSH:
20807fe6a43SSeth Howell 	default:
20907fe6a43SSeth Howell 		return -1;
21007fe6a43SSeth Howell 	}
21107fe6a43SSeth Howell 
21207fe6a43SSeth Howell 	SPDK_UNREACHABLE();
21307fe6a43SSeth Howell }
21407fe6a43SSeth Howell 
21507fe6a43SSeth Howell static void
bdev_virtio_submit_request(struct spdk_io_channel * ch,struct spdk_bdev_io * bdev_io)21607fe6a43SSeth Howell bdev_virtio_submit_request(struct spdk_io_channel *ch, struct spdk_bdev_io *bdev_io)
21707fe6a43SSeth Howell {
21807fe6a43SSeth Howell 	if (_bdev_virtio_submit_request(ch, bdev_io) < 0) {
21907fe6a43SSeth Howell 		spdk_bdev_io_complete(bdev_io, SPDK_BDEV_IO_STATUS_FAILED);
22007fe6a43SSeth Howell 	}
22107fe6a43SSeth Howell }
22207fe6a43SSeth Howell 
22307fe6a43SSeth Howell static bool
bdev_virtio_io_type_supported(void * ctx,enum spdk_bdev_io_type io_type)22407fe6a43SSeth Howell bdev_virtio_io_type_supported(void *ctx, enum spdk_bdev_io_type io_type)
22507fe6a43SSeth Howell {
22607fe6a43SSeth Howell 	struct virtio_blk_dev *bvdev = ctx;
22707fe6a43SSeth Howell 
22807fe6a43SSeth Howell 	switch (io_type) {
22907fe6a43SSeth Howell 	case SPDK_BDEV_IO_TYPE_READ:
23007fe6a43SSeth Howell 	case SPDK_BDEV_IO_TYPE_RESET:
23107fe6a43SSeth Howell 		return true;
23207fe6a43SSeth Howell 	case SPDK_BDEV_IO_TYPE_WRITE:
23307fe6a43SSeth Howell 		return !bvdev->readonly;
23407fe6a43SSeth Howell 	case SPDK_BDEV_IO_TYPE_UNMAP:
23507fe6a43SSeth Howell 		return bvdev->unmap;
23607fe6a43SSeth Howell 	case SPDK_BDEV_IO_TYPE_FLUSH:
23707fe6a43SSeth Howell 	default:
23807fe6a43SSeth Howell 		return false;
23907fe6a43SSeth Howell 	}
24007fe6a43SSeth Howell }
24107fe6a43SSeth Howell 
24207fe6a43SSeth Howell static struct spdk_io_channel *
bdev_virtio_get_io_channel(void * ctx)24307fe6a43SSeth Howell bdev_virtio_get_io_channel(void *ctx)
24407fe6a43SSeth Howell {
24507fe6a43SSeth Howell 	struct virtio_blk_dev *bvdev = ctx;
24607fe6a43SSeth Howell 
24707fe6a43SSeth Howell 	return spdk_get_io_channel(bvdev);
24807fe6a43SSeth Howell }
24907fe6a43SSeth Howell 
25007fe6a43SSeth Howell static void
virtio_blk_dev_unregister_cb(void * io_device)25107fe6a43SSeth Howell virtio_blk_dev_unregister_cb(void *io_device)
25207fe6a43SSeth Howell {
25307fe6a43SSeth Howell 	struct virtio_blk_dev *bvdev = io_device;
25407fe6a43SSeth Howell 	struct virtio_dev *vdev = &bvdev->vdev;
25507fe6a43SSeth Howell 
25607fe6a43SSeth Howell 	virtio_dev_stop(vdev);
25707fe6a43SSeth Howell 	virtio_dev_destruct(vdev);
25807fe6a43SSeth Howell 	spdk_bdev_destruct_done(&bvdev->bdev, 0);
25907fe6a43SSeth Howell 	free(bvdev);
26007fe6a43SSeth Howell }
26107fe6a43SSeth Howell 
26207fe6a43SSeth Howell static int
bdev_virtio_disk_destruct(void * ctx)26307fe6a43SSeth Howell bdev_virtio_disk_destruct(void *ctx)
26407fe6a43SSeth Howell {
26507fe6a43SSeth Howell 	struct virtio_blk_dev *bvdev = ctx;
26607fe6a43SSeth Howell 
26707fe6a43SSeth Howell 	spdk_io_device_unregister(bvdev, virtio_blk_dev_unregister_cb);
26807fe6a43SSeth Howell 	return 1;
26907fe6a43SSeth Howell }
27007fe6a43SSeth Howell 
27107fe6a43SSeth Howell int
bdev_virtio_blk_dev_remove(const char * name,bdev_virtio_remove_cb cb_fn,void * cb_arg)27207fe6a43SSeth Howell bdev_virtio_blk_dev_remove(const char *name, bdev_virtio_remove_cb cb_fn, void *cb_arg)
27307fe6a43SSeth Howell {
2744573e4ccSShuhei Matsumoto 	return spdk_bdev_unregister_by_name(name, &virtio_blk_if, cb_fn, cb_arg);
27507fe6a43SSeth Howell }
27607fe6a43SSeth Howell 
27707fe6a43SSeth Howell static int
bdev_virtio_dump_json_config(void * ctx,struct spdk_json_write_ctx * w)27807fe6a43SSeth Howell bdev_virtio_dump_json_config(void *ctx, struct spdk_json_write_ctx *w)
27907fe6a43SSeth Howell {
28007fe6a43SSeth Howell 	struct virtio_blk_dev *bvdev = ctx;
28107fe6a43SSeth Howell 
28207fe6a43SSeth Howell 	virtio_dev_dump_json_info(&bvdev->vdev, w);
28307fe6a43SSeth Howell 	return 0;
28407fe6a43SSeth Howell }
28507fe6a43SSeth Howell 
28607fe6a43SSeth Howell static void
bdev_virtio_write_config_json(struct spdk_bdev * bdev,struct spdk_json_write_ctx * w)28707fe6a43SSeth Howell bdev_virtio_write_config_json(struct spdk_bdev *bdev, struct spdk_json_write_ctx *w)
28807fe6a43SSeth Howell {
28907fe6a43SSeth Howell 	struct virtio_blk_dev *bvdev = bdev->ctxt;
29007fe6a43SSeth Howell 
29107fe6a43SSeth Howell 	spdk_json_write_object_begin(w);
29207fe6a43SSeth Howell 
2932aed03f0SMaciej Wawryk 	spdk_json_write_named_string(w, "method", "bdev_virtio_attach_controller");
29407fe6a43SSeth Howell 
29507fe6a43SSeth Howell 	spdk_json_write_named_object_begin(w, "params");
29607fe6a43SSeth Howell 	spdk_json_write_named_string(w, "name", bvdev->vdev.name);
29707fe6a43SSeth Howell 	spdk_json_write_named_string(w, "dev_type", "blk");
29807fe6a43SSeth Howell 
29907fe6a43SSeth Howell 	/* Write transport specific parameters. */
30007fe6a43SSeth Howell 	bvdev->vdev.backend_ops->write_json_config(&bvdev->vdev, w);
30107fe6a43SSeth Howell 
30207fe6a43SSeth Howell 	spdk_json_write_object_end(w);
30307fe6a43SSeth Howell 
30407fe6a43SSeth Howell 	spdk_json_write_object_end(w);
30507fe6a43SSeth Howell }
30607fe6a43SSeth Howell 
30707fe6a43SSeth Howell static const struct spdk_bdev_fn_table virtio_fn_table = {
30807fe6a43SSeth Howell 	.destruct		= bdev_virtio_disk_destruct,
30907fe6a43SSeth Howell 	.submit_request		= bdev_virtio_submit_request,
31007fe6a43SSeth Howell 	.io_type_supported	= bdev_virtio_io_type_supported,
31107fe6a43SSeth Howell 	.get_io_channel		= bdev_virtio_get_io_channel,
31207fe6a43SSeth Howell 	.dump_info_json		= bdev_virtio_dump_json_config,
31307fe6a43SSeth Howell 	.write_config_json	= bdev_virtio_write_config_json,
31407fe6a43SSeth Howell };
31507fe6a43SSeth Howell 
31607fe6a43SSeth Howell static void
bdev_virtio_io_cpl(struct spdk_bdev_io * bdev_io)31707fe6a43SSeth Howell bdev_virtio_io_cpl(struct spdk_bdev_io *bdev_io)
31807fe6a43SSeth Howell {
31907fe6a43SSeth Howell 	struct virtio_blk_io_ctx *io_ctx = (struct virtio_blk_io_ctx *)bdev_io->driver_ctx;
32007fe6a43SSeth Howell 
32107fe6a43SSeth Howell 	spdk_bdev_io_complete(bdev_io, io_ctx->resp == VIRTIO_BLK_S_OK ?
32207fe6a43SSeth Howell 			      SPDK_BDEV_IO_STATUS_SUCCESS : SPDK_BDEV_IO_STATUS_FAILED);
32307fe6a43SSeth Howell }
32407fe6a43SSeth Howell 
32507fe6a43SSeth Howell static int
bdev_virtio_poll(void * arg)32607fe6a43SSeth Howell bdev_virtio_poll(void *arg)
32707fe6a43SSeth Howell {
32807fe6a43SSeth Howell 	struct bdev_virtio_blk_io_channel *ch = arg;
32907fe6a43SSeth Howell 	void *io[32];
33007fe6a43SSeth Howell 	uint32_t io_len[32];
33107fe6a43SSeth Howell 	uint16_t i, cnt;
33207fe6a43SSeth Howell 
33307fe6a43SSeth Howell 	cnt = virtio_recv_pkts(ch->vq, io, io_len, SPDK_COUNTOF(io));
33407fe6a43SSeth Howell 	for (i = 0; i < cnt; ++i) {
33507fe6a43SSeth Howell 		bdev_virtio_io_cpl(io[i]);
33607fe6a43SSeth Howell 	}
33707fe6a43SSeth Howell 
33807fe6a43SSeth Howell 	return cnt;
33907fe6a43SSeth Howell }
34007fe6a43SSeth Howell 
34107fe6a43SSeth Howell static int
bdev_virtio_blk_ch_create_cb(void * io_device,void * ctx_buf)34207fe6a43SSeth Howell bdev_virtio_blk_ch_create_cb(void *io_device, void *ctx_buf)
34307fe6a43SSeth Howell {
34407fe6a43SSeth Howell 	struct virtio_blk_dev *bvdev = io_device;
34507fe6a43SSeth Howell 	struct virtio_dev *vdev = &bvdev->vdev;
34607fe6a43SSeth Howell 	struct bdev_virtio_blk_io_channel *ch = ctx_buf;
34707fe6a43SSeth Howell 	struct virtqueue *vq;
34807fe6a43SSeth Howell 	int32_t queue_idx;
34907fe6a43SSeth Howell 
35007fe6a43SSeth Howell 	queue_idx = virtio_dev_find_and_acquire_queue(vdev, 0);
35107fe6a43SSeth Howell 	if (queue_idx < 0) {
35207fe6a43SSeth Howell 		SPDK_ERRLOG("Couldn't get an unused queue for the io_channel.\n");
35307fe6a43SSeth Howell 		return -1;
35407fe6a43SSeth Howell 	}
35507fe6a43SSeth Howell 
35607fe6a43SSeth Howell 	vq = vdev->vqs[queue_idx];
35707fe6a43SSeth Howell 
35807fe6a43SSeth Howell 	ch->vdev = vdev;
35907fe6a43SSeth Howell 	ch->vq = vq;
36007fe6a43SSeth Howell 
361ab0bc5c2SShuhei Matsumoto 	ch->poller = SPDK_POLLER_REGISTER(bdev_virtio_poll, ch, 0);
36207fe6a43SSeth Howell 	return 0;
36307fe6a43SSeth Howell }
36407fe6a43SSeth Howell 
36507fe6a43SSeth Howell static void
bdev_virtio_blk_ch_destroy_cb(void * io_device,void * ctx_buf)36607fe6a43SSeth Howell bdev_virtio_blk_ch_destroy_cb(void *io_device, void *ctx_buf)
36707fe6a43SSeth Howell {
36807fe6a43SSeth Howell 	struct virtio_blk_dev *bvdev = io_device;
36907fe6a43SSeth Howell 	struct virtio_dev *vdev = &bvdev->vdev;
37007fe6a43SSeth Howell 	struct bdev_virtio_blk_io_channel *ch = ctx_buf;
37107fe6a43SSeth Howell 	struct virtqueue *vq = ch->vq;
37207fe6a43SSeth Howell 
37307fe6a43SSeth Howell 	spdk_poller_unregister(&ch->poller);
37407fe6a43SSeth Howell 	virtio_dev_release_queue(vdev, vq->vq_queue_index);
37507fe6a43SSeth Howell }
37607fe6a43SSeth Howell 
37707fe6a43SSeth Howell static int
virtio_blk_dev_init(struct virtio_blk_dev * bvdev,uint16_t max_queues)37807fe6a43SSeth Howell virtio_blk_dev_init(struct virtio_blk_dev *bvdev, uint16_t max_queues)
37907fe6a43SSeth Howell {
38007fe6a43SSeth Howell 	struct virtio_dev *vdev = &bvdev->vdev;
38107fe6a43SSeth Howell 	struct spdk_bdev *bdev = &bvdev->bdev;
38207fe6a43SSeth Howell 	uint64_t capacity, num_blocks;
383da625683SJin Yu 	uint32_t block_size, size_max, seg_max;
38407fe6a43SSeth Howell 	uint16_t host_max_queues;
38507fe6a43SSeth Howell 	int rc;
38607fe6a43SSeth Howell 
38707fe6a43SSeth Howell 	if (virtio_dev_has_feature(vdev, VIRTIO_BLK_F_BLK_SIZE)) {
38807fe6a43SSeth Howell 		rc = virtio_dev_read_dev_config(vdev, offsetof(struct virtio_blk_config, blk_size),
38907fe6a43SSeth Howell 						&block_size, sizeof(block_size));
39007fe6a43SSeth Howell 		if (rc) {
39107fe6a43SSeth Howell 			SPDK_ERRLOG("%s: config read failed: %s\n", vdev->name, spdk_strerror(-rc));
39207fe6a43SSeth Howell 			return rc;
39307fe6a43SSeth Howell 		}
39407fe6a43SSeth Howell 
39507fe6a43SSeth Howell 		if (block_size == 0 || block_size % 512 != 0) {
39607fe6a43SSeth Howell 			SPDK_ERRLOG("%s: invalid block size (%"PRIu32"). Must be "
39707fe6a43SSeth Howell 				    "a multiple of 512.\n", vdev->name, block_size);
39807fe6a43SSeth Howell 			return -EIO;
39907fe6a43SSeth Howell 		}
40007fe6a43SSeth Howell 	} else {
40107fe6a43SSeth Howell 		block_size = 512;
40207fe6a43SSeth Howell 	}
40307fe6a43SSeth Howell 
40407fe6a43SSeth Howell 	rc = virtio_dev_read_dev_config(vdev, offsetof(struct virtio_blk_config, capacity),
40507fe6a43SSeth Howell 					&capacity, sizeof(capacity));
40607fe6a43SSeth Howell 	if (rc) {
40707fe6a43SSeth Howell 		SPDK_ERRLOG("%s: config read failed: %s\n", vdev->name, spdk_strerror(-rc));
40807fe6a43SSeth Howell 		return rc;
40907fe6a43SSeth Howell 	}
41007fe6a43SSeth Howell 
41107fe6a43SSeth Howell 	/* `capacity` is a number of 512-byte sectors. */
41207fe6a43SSeth Howell 	num_blocks = capacity * 512 / block_size;
41307fe6a43SSeth Howell 	if (num_blocks == 0) {
41407fe6a43SSeth Howell 		SPDK_ERRLOG("%s: size too small (size: %"PRIu64", blocksize: %"PRIu32").\n",
41507fe6a43SSeth Howell 			    vdev->name, capacity * 512, block_size);
41607fe6a43SSeth Howell 		return -EIO;
41707fe6a43SSeth Howell 	}
41807fe6a43SSeth Howell 
41907fe6a43SSeth Howell 	if ((capacity * 512) % block_size != 0) {
42007fe6a43SSeth Howell 		SPDK_WARNLOG("%s: size has been rounded down to the nearest block size boundary. "
42107fe6a43SSeth Howell 			     "(block size: %"PRIu32", previous size: %"PRIu64", new size: %"PRIu64")\n",
42207fe6a43SSeth Howell 			     vdev->name, block_size, capacity * 512, num_blocks * block_size);
42307fe6a43SSeth Howell 	}
42407fe6a43SSeth Howell 
42507fe6a43SSeth Howell 	if (virtio_dev_has_feature(vdev, VIRTIO_BLK_F_MQ)) {
42607fe6a43SSeth Howell 		rc = virtio_dev_read_dev_config(vdev, offsetof(struct virtio_blk_config, num_queues),
42707fe6a43SSeth Howell 						&host_max_queues, sizeof(host_max_queues));
42807fe6a43SSeth Howell 		if (rc) {
42907fe6a43SSeth Howell 			SPDK_ERRLOG("%s: config read failed: %s\n", vdev->name, spdk_strerror(-rc));
43007fe6a43SSeth Howell 			return rc;
43107fe6a43SSeth Howell 		}
43207fe6a43SSeth Howell 	} else {
43307fe6a43SSeth Howell 		host_max_queues = 1;
43407fe6a43SSeth Howell 	}
43507fe6a43SSeth Howell 
436da625683SJin Yu 	if (virtio_dev_has_feature(vdev, VIRTIO_BLK_F_SIZE_MAX)) {
437da625683SJin Yu 		rc = virtio_dev_read_dev_config(vdev, offsetof(struct virtio_blk_config, size_max),
438da625683SJin Yu 						&size_max, sizeof(size_max));
439da625683SJin Yu 		if (rc) {
440da625683SJin Yu 			SPDK_ERRLOG("%s: config read failed: %s\n", vdev->name, spdk_strerror(-rc));
441da625683SJin Yu 			return rc;
442da625683SJin Yu 		}
443da625683SJin Yu 
444da625683SJin Yu 		if (spdk_unlikely(size_max < block_size)) {
445da625683SJin Yu 			SPDK_WARNLOG("%s: minimum segment size is set to block size %u forcefully.\n",
446da625683SJin Yu 				     vdev->name, block_size);
447da625683SJin Yu 			size_max = block_size;
448da625683SJin Yu 		}
449da625683SJin Yu 
450da625683SJin Yu 		bdev->max_segment_size = size_max;
451da625683SJin Yu 	}
452da625683SJin Yu 
453da625683SJin Yu 	if (virtio_dev_has_feature(vdev, VIRTIO_BLK_F_SEG_MAX)) {
454da625683SJin Yu 		rc = virtio_dev_read_dev_config(vdev, offsetof(struct virtio_blk_config, seg_max),
455da625683SJin Yu 						&seg_max, sizeof(seg_max));
456da625683SJin Yu 		if (rc) {
457da625683SJin Yu 			SPDK_ERRLOG("%s: config read failed: %s\n", vdev->name, spdk_strerror(-rc));
458da625683SJin Yu 			return rc;
459da625683SJin Yu 		}
460da625683SJin Yu 
461da625683SJin Yu 		if (spdk_unlikely(seg_max == 0)) {
462da625683SJin Yu 			SPDK_ERRLOG("%s: virtio blk SEG_MAX can't be 0\n", vdev->name);
463da625683SJin Yu 			return -EINVAL;
464da625683SJin Yu 		}
465da625683SJin Yu 
466da625683SJin Yu 		bdev->max_num_segments = seg_max;
467da625683SJin Yu 	}
468da625683SJin Yu 
46907fe6a43SSeth Howell 	if (virtio_dev_has_feature(vdev, VIRTIO_BLK_F_RO)) {
47007fe6a43SSeth Howell 		bvdev->readonly = true;
47107fe6a43SSeth Howell 	}
47207fe6a43SSeth Howell 
47307fe6a43SSeth Howell 	if (virtio_dev_has_feature(vdev, VIRTIO_BLK_F_DISCARD)) {
47407fe6a43SSeth Howell 		bvdev->unmap = true;
47507fe6a43SSeth Howell 	}
47607fe6a43SSeth Howell 
47707fe6a43SSeth Howell 	if (max_queues == 0) {
47807fe6a43SSeth Howell 		SPDK_ERRLOG("%s: requested 0 request queues (%"PRIu16" available).\n",
47907fe6a43SSeth Howell 			    vdev->name, host_max_queues);
48007fe6a43SSeth Howell 		return -EINVAL;
48107fe6a43SSeth Howell 	}
48207fe6a43SSeth Howell 
48307fe6a43SSeth Howell 	if (max_queues > host_max_queues) {
48407fe6a43SSeth Howell 		SPDK_WARNLOG("%s: requested %"PRIu16" request queues "
48507fe6a43SSeth Howell 			     "but only %"PRIu16" available.\n",
48607fe6a43SSeth Howell 			     vdev->name, max_queues, host_max_queues);
48707fe6a43SSeth Howell 		max_queues = host_max_queues;
48807fe6a43SSeth Howell 	}
48907fe6a43SSeth Howell 
49007fe6a43SSeth Howell 	/* bdev is tied with the virtio device; we can reuse the name */
49107fe6a43SSeth Howell 	bdev->name = vdev->name;
49207fe6a43SSeth Howell 	rc = virtio_dev_start(vdev, max_queues, 0);
49307fe6a43SSeth Howell 	if (rc != 0) {
49407fe6a43SSeth Howell 		return rc;
49507fe6a43SSeth Howell 	}
49607fe6a43SSeth Howell 
49707fe6a43SSeth Howell 	bdev->product_name = "VirtioBlk Disk";
49807fe6a43SSeth Howell 	bdev->write_cache = 0;
49907fe6a43SSeth Howell 	bdev->blocklen = block_size;
50007fe6a43SSeth Howell 	bdev->blockcnt = num_blocks;
50107fe6a43SSeth Howell 
50207fe6a43SSeth Howell 	bdev->ctxt = bvdev;
50307fe6a43SSeth Howell 	bdev->fn_table = &virtio_fn_table;
50407fe6a43SSeth Howell 	bdev->module = &virtio_blk_if;
50507fe6a43SSeth Howell 
50607fe6a43SSeth Howell 	spdk_io_device_register(bvdev, bdev_virtio_blk_ch_create_cb,
50707fe6a43SSeth Howell 				bdev_virtio_blk_ch_destroy_cb,
50807fe6a43SSeth Howell 				sizeof(struct bdev_virtio_blk_io_channel),
50907fe6a43SSeth Howell 				vdev->name);
51007fe6a43SSeth Howell 
51107fe6a43SSeth Howell 	rc = spdk_bdev_register(bdev);
51207fe6a43SSeth Howell 	if (rc) {
51307fe6a43SSeth Howell 		SPDK_ERRLOG("Failed to register bdev name=%s\n", bdev->name);
51407fe6a43SSeth Howell 		spdk_io_device_unregister(bvdev, NULL);
51507fe6a43SSeth Howell 		virtio_dev_stop(vdev);
51607fe6a43SSeth Howell 		return rc;
51707fe6a43SSeth Howell 	}
51807fe6a43SSeth Howell 
51907fe6a43SSeth Howell 	return 0;
52007fe6a43SSeth Howell }
52107fe6a43SSeth Howell 
52207fe6a43SSeth Howell static struct virtio_blk_dev *
virtio_pci_blk_dev_create(const char * name,struct virtio_pci_ctx * pci_ctx)52307fe6a43SSeth Howell virtio_pci_blk_dev_create(const char *name, struct virtio_pci_ctx *pci_ctx)
52407fe6a43SSeth Howell {
52507fe6a43SSeth Howell 	static int pci_dev_counter = 0;
52607fe6a43SSeth Howell 	struct virtio_blk_dev *bvdev;
52707fe6a43SSeth Howell 	struct virtio_dev *vdev;
52807fe6a43SSeth Howell 	char *default_name = NULL;
52907fe6a43SSeth Howell 	uint16_t num_queues;
53007fe6a43SSeth Howell 	int rc;
53107fe6a43SSeth Howell 
53207fe6a43SSeth Howell 	bvdev = calloc(1, sizeof(*bvdev));
53307fe6a43SSeth Howell 	if (bvdev == NULL) {
53407fe6a43SSeth Howell 		SPDK_ERRLOG("virtio device calloc failed\n");
53507fe6a43SSeth Howell 		return NULL;
53607fe6a43SSeth Howell 	}
53707fe6a43SSeth Howell 	vdev = &bvdev->vdev;
53807fe6a43SSeth Howell 
53907fe6a43SSeth Howell 	if (name == NULL) {
54007fe6a43SSeth Howell 		default_name = spdk_sprintf_alloc("VirtioBlk%"PRIu32, pci_dev_counter++);
54107fe6a43SSeth Howell 		if (default_name == NULL) {
54207fe6a43SSeth Howell 			free(vdev);
54307fe6a43SSeth Howell 			return NULL;
54407fe6a43SSeth Howell 		}
54507fe6a43SSeth Howell 		name = default_name;
54607fe6a43SSeth Howell 	}
54707fe6a43SSeth Howell 
54807fe6a43SSeth Howell 	rc = virtio_pci_dev_init(vdev, name, pci_ctx);
54907fe6a43SSeth Howell 	free(default_name);
55007fe6a43SSeth Howell 
55107fe6a43SSeth Howell 	if (rc != 0) {
55207fe6a43SSeth Howell 		free(bvdev);
55307fe6a43SSeth Howell 		return NULL;
55407fe6a43SSeth Howell 	}
55507fe6a43SSeth Howell 
55607fe6a43SSeth Howell 	rc = virtio_dev_reset(vdev, VIRTIO_BLK_DEV_SUPPORTED_FEATURES);
55707fe6a43SSeth Howell 	if (rc != 0) {
55879c7744eSJin Yu 		goto fail;
55907fe6a43SSeth Howell 	}
56007fe6a43SSeth Howell 
56107fe6a43SSeth Howell 	/* TODO: add a way to limit usable virtqueues */
56207fe6a43SSeth Howell 	if (virtio_dev_has_feature(vdev, VIRTIO_BLK_F_MQ)) {
56307fe6a43SSeth Howell 		rc = virtio_dev_read_dev_config(vdev, offsetof(struct virtio_blk_config, num_queues),
56407fe6a43SSeth Howell 						&num_queues, sizeof(num_queues));
56507fe6a43SSeth Howell 		if (rc) {
56607fe6a43SSeth Howell 			SPDK_ERRLOG("%s: config read failed: %s\n", vdev->name, spdk_strerror(-rc));
56779c7744eSJin Yu 			goto fail;
56807fe6a43SSeth Howell 		}
56907fe6a43SSeth Howell 	} else {
57007fe6a43SSeth Howell 		num_queues = 1;
57107fe6a43SSeth Howell 	}
57207fe6a43SSeth Howell 
57307fe6a43SSeth Howell 	rc = virtio_blk_dev_init(bvdev, num_queues);
57407fe6a43SSeth Howell 	if (rc != 0) {
57579c7744eSJin Yu 		goto fail;
57607fe6a43SSeth Howell 	}
57707fe6a43SSeth Howell 
57807fe6a43SSeth Howell 	return bvdev;
57979c7744eSJin Yu 
58079c7744eSJin Yu fail:
58179c7744eSJin Yu 	vdev->ctx = NULL;
58279c7744eSJin Yu 	virtio_dev_destruct(vdev);
58379c7744eSJin Yu 	free(bvdev);
58479c7744eSJin Yu 	return NULL;
58507fe6a43SSeth Howell }
58607fe6a43SSeth Howell 
58707fe6a43SSeth Howell static struct virtio_blk_dev *
virtio_user_blk_dev_create(const char * name,const char * path,uint16_t num_queues,uint32_t queue_size)58807fe6a43SSeth Howell virtio_user_blk_dev_create(const char *name, const char *path,
58907fe6a43SSeth Howell 			   uint16_t num_queues, uint32_t queue_size)
59007fe6a43SSeth Howell {
59107fe6a43SSeth Howell 	struct virtio_blk_dev *bvdev;
592515d028eSChangpeng Liu 	uint64_t feature_bits;
59307fe6a43SSeth Howell 	int rc;
59407fe6a43SSeth Howell 
59507fe6a43SSeth Howell 	bvdev = calloc(1, sizeof(*bvdev));
59607fe6a43SSeth Howell 	if (bvdev == NULL) {
59707fe6a43SSeth Howell 		SPDK_ERRLOG("calloc failed for virtio device %s: %s\n", name, path);
59807fe6a43SSeth Howell 		return NULL;
59907fe6a43SSeth Howell 	}
60007fe6a43SSeth Howell 
60107fe6a43SSeth Howell 	rc = virtio_user_dev_init(&bvdev->vdev, name, path, queue_size);
60207fe6a43SSeth Howell 	if (rc != 0) {
60307fe6a43SSeth Howell 		SPDK_ERRLOG("Failed to create virito device %s: %s\n", name, path);
60407fe6a43SSeth Howell 		free(bvdev);
60507fe6a43SSeth Howell 		return NULL;
60607fe6a43SSeth Howell 	}
60707fe6a43SSeth Howell 
608515d028eSChangpeng Liu 	feature_bits = VIRTIO_BLK_DEV_SUPPORTED_FEATURES;
609515d028eSChangpeng Liu 	feature_bits |= (1ULL << VHOST_USER_F_PROTOCOL_FEATURES);
610515d028eSChangpeng Liu 	rc = virtio_dev_reset(&bvdev->vdev, feature_bits);
61107fe6a43SSeth Howell 	if (rc != 0) {
61207fe6a43SSeth Howell 		virtio_dev_destruct(&bvdev->vdev);
61307fe6a43SSeth Howell 		free(bvdev);
61407fe6a43SSeth Howell 		return NULL;
61507fe6a43SSeth Howell 	}
61607fe6a43SSeth Howell 
61707fe6a43SSeth Howell 	rc = virtio_blk_dev_init(bvdev, num_queues);
61807fe6a43SSeth Howell 	if (rc != 0) {
61907fe6a43SSeth Howell 		virtio_dev_destruct(&bvdev->vdev);
62007fe6a43SSeth Howell 		free(bvdev);
62107fe6a43SSeth Howell 		return NULL;
62207fe6a43SSeth Howell 	}
62307fe6a43SSeth Howell 
62407fe6a43SSeth Howell 	return bvdev;
62507fe6a43SSeth Howell }
62607fe6a43SSeth Howell 
62707fe6a43SSeth Howell struct bdev_virtio_pci_dev_create_ctx {
62807fe6a43SSeth Howell 	const char *name;
62907fe6a43SSeth Howell 	struct virtio_blk_dev *ret;
63007fe6a43SSeth Howell };
63107fe6a43SSeth Howell 
63207fe6a43SSeth Howell static int
bdev_virtio_pci_blk_dev_create_cb(struct virtio_pci_ctx * pci_ctx,void * ctx)63307fe6a43SSeth Howell bdev_virtio_pci_blk_dev_create_cb(struct virtio_pci_ctx *pci_ctx, void *ctx)
63407fe6a43SSeth Howell {
63507fe6a43SSeth Howell 	struct bdev_virtio_pci_dev_create_ctx *create_ctx = ctx;
63607fe6a43SSeth Howell 
63707fe6a43SSeth Howell 	create_ctx->ret = virtio_pci_blk_dev_create(create_ctx->name, pci_ctx);
63807fe6a43SSeth Howell 	if (create_ctx->ret == NULL) {
63907fe6a43SSeth Howell 		return -1;
64007fe6a43SSeth Howell 	}
64107fe6a43SSeth Howell 
64207fe6a43SSeth Howell 	return 0;
64307fe6a43SSeth Howell }
64407fe6a43SSeth Howell 
64507fe6a43SSeth Howell struct spdk_bdev *
bdev_virtio_pci_blk_dev_create(const char * name,struct spdk_pci_addr * pci_addr)64607fe6a43SSeth Howell bdev_virtio_pci_blk_dev_create(const char *name, struct spdk_pci_addr *pci_addr)
64707fe6a43SSeth Howell {
64807fe6a43SSeth Howell 	struct bdev_virtio_pci_dev_create_ctx create_ctx;
64907fe6a43SSeth Howell 
65007fe6a43SSeth Howell 	create_ctx.name = name;
65107fe6a43SSeth Howell 	create_ctx.ret = NULL;
65207fe6a43SSeth Howell 
65307fe6a43SSeth Howell 	virtio_pci_dev_attach(bdev_virtio_pci_blk_dev_create_cb, &create_ctx,
6544c890c31SJin Yu 			      VIRTIO_ID_BLOCK, pci_addr);
65507fe6a43SSeth Howell 
65607fe6a43SSeth Howell 	if (create_ctx.ret == NULL) {
65707fe6a43SSeth Howell 		return NULL;
65807fe6a43SSeth Howell 	}
65907fe6a43SSeth Howell 
66007fe6a43SSeth Howell 	return &create_ctx.ret->bdev;
66107fe6a43SSeth Howell }
66207fe6a43SSeth Howell 
66307fe6a43SSeth Howell static int
bdev_virtio_pci_blk_monitor(void * arg)664ebea4dd6SJin Yu bdev_virtio_pci_blk_monitor(void *arg)
665ebea4dd6SJin Yu {
666ebea4dd6SJin Yu 	const char *vdev_name;
667ebea4dd6SJin Yu 	struct bdev_virtio_pci_dev_create_ctx create_ctx;
668ebea4dd6SJin Yu 
669ebea4dd6SJin Yu 	while ((vdev_name = virtio_pci_dev_event_process(g_blk_hotplug_fd, VIRTIO_ID_BLOCK)) != NULL) {
670ebea4dd6SJin Yu 		bdev_virtio_blk_dev_remove(vdev_name, NULL, NULL);
671ebea4dd6SJin Yu 	}
672ebea4dd6SJin Yu 
673ebea4dd6SJin Yu 	/* Enumerate virtio pci_blk device */
674ebea4dd6SJin Yu 	memset(&create_ctx, 0, sizeof(create_ctx));
675ebea4dd6SJin Yu 	virtio_pci_dev_enumerate(bdev_virtio_pci_blk_dev_create_cb, &create_ctx,
676ebea4dd6SJin Yu 				 VIRTIO_ID_BLOCK);
677ebea4dd6SJin Yu 
678ebea4dd6SJin Yu 	return SPDK_POLLER_BUSY;
679ebea4dd6SJin Yu }
680ebea4dd6SJin Yu 
681ebea4dd6SJin Yu int
bdev_virtio_pci_blk_set_hotplug(bool enabled,uint64_t period_us)682ebea4dd6SJin Yu bdev_virtio_pci_blk_set_hotplug(bool enabled, uint64_t period_us)
683ebea4dd6SJin Yu {
684ebea4dd6SJin Yu 	if (enabled == true && !spdk_process_is_primary()) {
685ebea4dd6SJin Yu 		return -EPERM;
686ebea4dd6SJin Yu 	}
687ebea4dd6SJin Yu 
688ebea4dd6SJin Yu 	if (g_blk_hotplug_poller) {
689ebea4dd6SJin Yu 		close(g_blk_hotplug_fd);
690ebea4dd6SJin Yu 		spdk_poller_unregister(&g_blk_hotplug_poller);
691ebea4dd6SJin Yu 	}
692ebea4dd6SJin Yu 
693ebea4dd6SJin Yu 	if (!enabled) {
694ebea4dd6SJin Yu 		return 0;
695ebea4dd6SJin Yu 	}
696ebea4dd6SJin Yu 
697ebea4dd6SJin Yu 	g_blk_hotplug_fd = spdk_pci_event_listen();
698ebea4dd6SJin Yu 	if (g_blk_hotplug_fd < 0) {
699ebea4dd6SJin Yu 		return g_blk_hotplug_fd;
700ebea4dd6SJin Yu 	}
701ebea4dd6SJin Yu 
702ebea4dd6SJin Yu 	period_us = period_us ? period_us : VIRTIO_BLK_HOTPLUG_POLL_PERIOD_DEFAULT;
703ebea4dd6SJin Yu 	period_us = spdk_min(period_us, VIRTIO_BLK_HOTPLUG_POLL_PERIOD_MAX);
704ebea4dd6SJin Yu 	g_blk_hotplug_poller = spdk_poller_register(bdev_virtio_pci_blk_monitor, NULL, period_us);
705ebea4dd6SJin Yu 	if (!g_blk_hotplug_poller) {
706ebea4dd6SJin Yu 		close(g_blk_hotplug_fd);
707ebea4dd6SJin Yu 		return -1;
708ebea4dd6SJin Yu 	}
709ebea4dd6SJin Yu 
710ebea4dd6SJin Yu 	return 0;
711ebea4dd6SJin Yu }
712ebea4dd6SJin Yu 
713ebea4dd6SJin Yu static int
bdev_virtio_initialize(void)71407fe6a43SSeth Howell bdev_virtio_initialize(void)
71507fe6a43SSeth Howell {
71607fe6a43SSeth Howell 	return 0;
71707fe6a43SSeth Howell }
71807fe6a43SSeth Howell 
71907fe6a43SSeth Howell struct spdk_bdev *
bdev_virtio_user_blk_dev_create(const char * name,const char * path,unsigned num_queues,unsigned queue_size)72007fe6a43SSeth Howell bdev_virtio_user_blk_dev_create(const char *name, const char *path,
72107fe6a43SSeth Howell 				unsigned num_queues, unsigned queue_size)
72207fe6a43SSeth Howell {
72307fe6a43SSeth Howell 	struct virtio_blk_dev *bvdev;
72407fe6a43SSeth Howell 
72507fe6a43SSeth Howell 	bvdev = virtio_user_blk_dev_create(name, path, num_queues, queue_size);
72607fe6a43SSeth Howell 	if (bvdev == NULL) {
72707fe6a43SSeth Howell 		return NULL;
72807fe6a43SSeth Howell 	}
72907fe6a43SSeth Howell 
73007fe6a43SSeth Howell 	return &bvdev->bdev;
73107fe6a43SSeth Howell }
73207fe6a43SSeth Howell 
733295e54d1SChangpeng Liu struct spdk_bdev *
bdev_virtio_vfio_user_blk_dev_create(const char * name,const char * path)734295e54d1SChangpeng Liu bdev_virtio_vfio_user_blk_dev_create(const char *name, const char *path)
735295e54d1SChangpeng Liu {
736295e54d1SChangpeng Liu 	struct virtio_blk_dev *bvdev;
737295e54d1SChangpeng Liu 	uint16_t num_queues = 0;
738295e54d1SChangpeng Liu 	int rc;
739295e54d1SChangpeng Liu 
740295e54d1SChangpeng Liu 	bvdev = calloc(1, sizeof(*bvdev));
741295e54d1SChangpeng Liu 	if (bvdev == NULL) {
742295e54d1SChangpeng Liu 		SPDK_ERRLOG("calloc failed for virtio device %s: %s\n", name, path);
743295e54d1SChangpeng Liu 		return NULL;
744295e54d1SChangpeng Liu 	}
745295e54d1SChangpeng Liu 
746295e54d1SChangpeng Liu 	rc = virtio_vfio_user_dev_init(&bvdev->vdev, name, path);
747295e54d1SChangpeng Liu 	if (rc != 0) {
748295e54d1SChangpeng Liu 		SPDK_ERRLOG("Failed to create %s as virtio device\n", path);
749295e54d1SChangpeng Liu 		free(bvdev);
750295e54d1SChangpeng Liu 		return NULL;
751295e54d1SChangpeng Liu 	}
752295e54d1SChangpeng Liu 
753295e54d1SChangpeng Liu 	rc = virtio_dev_reset(&bvdev->vdev, VIRTIO_BLK_DEV_SUPPORTED_FEATURES);
754295e54d1SChangpeng Liu 	if (rc != 0) {
755295e54d1SChangpeng Liu 		SPDK_ERRLOG("Failed to reset %s as virtio device\n", path);
756295e54d1SChangpeng Liu 		virtio_dev_destruct(&bvdev->vdev);
757295e54d1SChangpeng Liu 		free(bvdev);
758295e54d1SChangpeng Liu 		return NULL;
759295e54d1SChangpeng Liu 	}
760295e54d1SChangpeng Liu 
761295e54d1SChangpeng Liu 	if (virtio_dev_has_feature(&bvdev->vdev, VIRTIO_BLK_F_MQ)) {
762295e54d1SChangpeng Liu 		rc = virtio_dev_read_dev_config(&bvdev->vdev, offsetof(struct virtio_blk_config, num_queues),
763295e54d1SChangpeng Liu 						&num_queues, sizeof(num_queues));
764295e54d1SChangpeng Liu 		if (rc) {
765295e54d1SChangpeng Liu 			SPDK_ERRLOG("%s: config read failed: %s\n", name, spdk_strerror(-rc));
766295e54d1SChangpeng Liu 			virtio_dev_destruct(&bvdev->vdev);
767295e54d1SChangpeng Liu 			free(bvdev);
768295e54d1SChangpeng Liu 			return NULL;
769295e54d1SChangpeng Liu 		}
770295e54d1SChangpeng Liu 	} else {
771295e54d1SChangpeng Liu 		num_queues = 1;
772295e54d1SChangpeng Liu 	}
773295e54d1SChangpeng Liu 
774295e54d1SChangpeng Liu 	rc = virtio_blk_dev_init(bvdev, num_queues);
775295e54d1SChangpeng Liu 	if (rc != 0) {
776295e54d1SChangpeng Liu 		SPDK_ERRLOG("Failed to initialize %s as virtio device\n", path);
777295e54d1SChangpeng Liu 		virtio_dev_destruct(&bvdev->vdev);
778295e54d1SChangpeng Liu 		free(bvdev);
779295e54d1SChangpeng Liu 		return NULL;
780295e54d1SChangpeng Liu 	}
781295e54d1SChangpeng Liu 
782295e54d1SChangpeng Liu 	return &bvdev->bdev;
783295e54d1SChangpeng Liu }
784295e54d1SChangpeng Liu 
78507fe6a43SSeth Howell static int
bdev_virtio_blk_get_ctx_size(void)78607fe6a43SSeth Howell bdev_virtio_blk_get_ctx_size(void)
78707fe6a43SSeth Howell {
78807fe6a43SSeth Howell 	return sizeof(struct virtio_blk_io_ctx);
78907fe6a43SSeth Howell }
79007fe6a43SSeth Howell 
7912172c432STomasz Zawadzki SPDK_LOG_REGISTER_COMPONENT(virtio_blk)
792