diff --git a/doc/jsonrpc.md b/doc/jsonrpc.md index 5d040e12c..a3b2d79a5 100644 --- a/doc/jsonrpc.md +++ b/doc/jsonrpc.md @@ -7576,6 +7576,41 @@ Example response: } ~~~ +### virtio_blk_create_transport {#rpc_virtio_blk_create_transport} + +Create virtio blk transport. + +#### Parameters + +Name | Optional | Type | Description +----------------------- | -------- | ----------- | ----------- +name | Required | string | Transport name + +#### Example + +Example request: + +~~~json +{ + "params": { + "name": "vhost_user_blk" + }, + "jsonrpc": "2.0", + "method": "virtio_blk_create_transport", + "id": 1 +} +~~~ + +Example response: + +~~~json +{ + "jsonrpc": "2.0", + "id": 1, + "result": true +} +~~~ + ### vhost_create_blk_controller {#rpc_vhost_create_blk_controller} Create vhost block controller @@ -7591,6 +7626,7 @@ ctrlr | Required | string | Controller name bdev_name | Required | string | Name of bdev to expose block device readonly | Optional | boolean | If true, this target will be read only (default: false) cpumask | Optional | string | @ref cpu_mask for this controller +transport | Optional | string | virtio blk transport name (default: vhost_user_blk) #### Example diff --git a/include/spdk/vhost.h b/include/spdk/vhost.h index 800a904f3..f72ac8050 100644 --- a/include/spdk/vhost.h +++ b/include/spdk/vhost.h @@ -332,6 +332,7 @@ int spdk_vhost_scsi_dev_remove_tgt(struct spdk_vhost_dev *vdev, unsigned scsi_tg * is allowed but not required. The mask itself can be constructed as: * ((1 << cpu0) | (1 << cpu1) | ... | (1 << cpuN)). * \param dev_name bdev name to associate with this vhost device + * \param transport virtio blk transport name (default: vhost_user_blk) * \param params JSON value object containing variables: * readonly if set, all writes to the device will fail with * \c VIRTIO_BLK_S_IOERR error code. @@ -341,7 +342,7 @@ int spdk_vhost_scsi_dev_remove_tgt(struct spdk_vhost_dev *vdev, unsigned scsi_tg * \return 0 on success, negative errno on error. */ int spdk_vhost_blk_construct(const char *name, const char *cpumask, const char *dev_name, - const struct spdk_json_val *params); + const char *transport, const struct spdk_json_val *params); /** * Remove a vhost device. The device must not have any open connections on it's socket. diff --git a/lib/vhost/vhost.c b/lib/vhost/vhost.c index 0862923d7..f63ebb90f 100644 --- a/lib/vhost/vhost.c +++ b/lib/vhost/vhost.c @@ -48,6 +48,9 @@ static TAILQ_HEAD(, spdk_vhost_dev) g_vhost_devices = TAILQ_HEAD_INITIALIZER( g_vhost_devices); static pthread_mutex_t g_vhost_mutex = PTHREAD_MUTEX_INITIALIZER; +static TAILQ_HEAD(, spdk_virtio_blk_transport) g_virtio_blk_transports = TAILQ_HEAD_INITIALIZER( + g_virtio_blk_transports); + struct spdk_vhost_dev * spdk_vhost_dev_next(struct spdk_vhost_dev *vdev) { @@ -114,8 +117,24 @@ vhost_parse_core_mask(const char *mask, struct spdk_cpuset *cpumask) return 0; } +TAILQ_HEAD(, virtio_blk_transport_ops_list_element) +g_spdk_virtio_blk_transport_ops = TAILQ_HEAD_INITIALIZER(g_spdk_virtio_blk_transport_ops); + +const struct spdk_virtio_blk_transport_ops * +virtio_blk_get_transport_ops(const char *transport_name) +{ + struct virtio_blk_transport_ops_list_element *ops; + TAILQ_FOREACH(ops, &g_spdk_virtio_blk_transport_ops, link) { + if (strcasecmp(transport_name, ops->ops.name) == 0) { + return &ops->ops; + } + } + return NULL; +} + int vhost_dev_register(struct spdk_vhost_dev *vdev, const char *name, const char *mask_str, + const struct spdk_json_val *params, const struct spdk_vhost_dev_backend *backend, const struct spdk_vhost_user_dev_backend *user_backend) { @@ -145,8 +164,11 @@ vhost_dev_register(struct spdk_vhost_dev *vdev, const char *name, const char *ma } vdev->backend = backend; - - rc = vhost_user_dev_register(vdev, name, &cpumask, user_backend); + if (vdev->backend->type == VHOST_BACKEND_SCSI) { + rc = vhost_user_dev_register(vdev, name, &cpumask, user_backend); + } else { + rc = virtio_blk_construct_ctrlr(vdev, name, &cpumask, params, user_backend); + } if (rc != 0) { free(vdev->name); return rc; @@ -163,7 +185,11 @@ vhost_dev_unregister(struct spdk_vhost_dev *vdev) { int rc; - rc = vhost_user_dev_unregister(vdev); + if (vdev->backend->type == VHOST_BACKEND_SCSI) { + rc = vhost_user_dev_unregister(vdev); + } else { + rc = virtio_blk_destroy_ctrlr(vdev); + } if (rc != 0) { return rc; } @@ -265,7 +291,7 @@ spdk_vhost_blk_init(spdk_vhost_init_cb init_cb) uint32_t i; int ret = 0; - ret = vhost_user_init(); + ret = virtio_blk_transport_create("vhost_user_blk", NULL); if (ret != 0) { goto out; } @@ -286,12 +312,25 @@ spdk_vhost_scsi_fini(spdk_vhost_fini_cb fini_cb) vhost_user_fini(vhost_fini); } +static void +virtio_blk_transports_destroy(void) +{ + struct spdk_virtio_blk_transport *transport = TAILQ_FIRST(&g_virtio_blk_transports); + + if (transport == NULL) { + g_fini_cb(); + return; + } + TAILQ_REMOVE(&g_virtio_blk_transports, transport, tailq); + virtio_blk_transport_destroy(transport, virtio_blk_transports_destroy); +} + void spdk_vhost_blk_fini(spdk_vhost_fini_cb fini_cb) { g_fini_cb = fini_cb; - vhost_user_fini(vhost_fini); + virtio_blk_transports_destroy(); } static void @@ -355,5 +394,65 @@ spdk_vhost_blk_config_json(struct spdk_json_write_ctx *w) spdk_json_write_array_end(w); } +void +virtio_blk_transport_register(const struct spdk_virtio_blk_transport_ops *ops) +{ + struct virtio_blk_transport_ops_list_element *new_ops; + + if (virtio_blk_get_transport_ops(ops->name) != NULL) { + SPDK_ERRLOG("Double registering virtio blk transport type %s.\n", ops->name); + assert(false); + return; + } + + new_ops = calloc(1, sizeof(*new_ops)); + if (new_ops == NULL) { + SPDK_ERRLOG("Unable to allocate memory to register new transport type %s.\n", ops->name); + assert(false); + return; + } + + new_ops->ops = *ops; + + TAILQ_INSERT_TAIL(&g_spdk_virtio_blk_transport_ops, new_ops, link); +} + +int +virtio_blk_transport_create(const char *transport_name, + const struct spdk_json_val *params) +{ + const struct spdk_virtio_blk_transport_ops *ops = NULL; + struct spdk_virtio_blk_transport *transport; + + TAILQ_FOREACH(transport, &g_virtio_blk_transports, tailq) { + if (strcasecmp(transport->ops->name, transport_name) == 0) { + return -EEXIST; + } + } + + ops = virtio_blk_get_transport_ops(transport_name); + if (!ops) { + SPDK_ERRLOG("Transport type '%s' unavailable.\n", transport_name); + return -ENOENT; + } + + transport = ops->create(params); + if (!transport) { + SPDK_ERRLOG("Unable to create new transport of type %s\n", transport_name); + return -EPERM; + } + + transport->ops = ops; + TAILQ_INSERT_TAIL(&g_virtio_blk_transports, transport, tailq); + return 0; +} + +int +virtio_blk_transport_destroy(struct spdk_virtio_blk_transport *transport, + spdk_vhost_fini_cb cb_fn) +{ + return transport->ops->destroy(transport, cb_fn); +} + SPDK_LOG_REGISTER_COMPONENT(vhost) SPDK_LOG_REGISTER_COMPONENT(vhost_ring) diff --git a/lib/vhost/vhost_blk.c b/lib/vhost/vhost_blk.c index 429ae3723..994167b86 100644 --- a/lib/vhost/vhost_blk.c +++ b/lib/vhost/vhost_blk.c @@ -62,6 +62,8 @@ #define SPDK_VHOST_BLK_PROTOCOL_FEATURES ((1ULL << VHOST_USER_PROTOCOL_F_CONFIG) | \ (1ULL << VHOST_USER_PROTOCOL_F_INFLIGHT_SHMFD)) +#define VIRTIO_BLK_DEFAULT_TRANSPORT "vhost_user_blk" + struct spdk_vhost_user_blk_task { struct spdk_vhost_blk_task blk_task; struct spdk_vhost_blk_session *bvsession; @@ -80,6 +82,8 @@ struct spdk_vhost_blk_dev { struct spdk_vhost_dev vdev; struct spdk_bdev *bdev; struct spdk_bdev_desc *bdev_desc; + const struct spdk_virtio_blk_transport_ops *ops; + /* dummy_io_channel is used to hold a bdev reference */ struct spdk_io_channel *dummy_io_channel; bool readonly; @@ -94,24 +98,11 @@ struct spdk_vhost_blk_session { struct spdk_poller *stop_poller; }; -struct rpc_vhost_blk { - bool readonly; - bool packed_ring; - bool packed_ring_recovery; -}; - -static const struct spdk_json_object_decoder rpc_construct_vhost_blk[] = { - {"readonly", offsetof(struct rpc_vhost_blk, readonly), spdk_json_decode_bool, true}, - {"packed_ring", offsetof(struct rpc_vhost_blk, packed_ring), spdk_json_decode_bool, true}, - {"packed_ring_recovery", offsetof(struct rpc_vhost_blk, packed_ring_recovery), spdk_json_decode_bool, true}, -}; - /* forward declaration */ static const struct spdk_vhost_dev_backend vhost_blk_device_backend; -static int -virtio_blk_process_request(struct spdk_vhost_dev *vdev, struct spdk_io_channel *ch, - struct spdk_vhost_blk_task *task); +static void +vhost_user_blk_request_finish(uint8_t status, struct spdk_vhost_blk_task *task, void *cb_arg); static int vhost_user_process_blk_request(struct spdk_vhost_user_blk_task *user_task) @@ -119,7 +110,8 @@ vhost_user_process_blk_request(struct spdk_vhost_user_blk_task *user_task) struct spdk_vhost_blk_session *bvsession = user_task->bvsession; struct spdk_vhost_dev *vdev = &bvsession->bvdev->vdev; - return virtio_blk_process_request(vdev, bvsession->io_channel, &user_task->blk_task); + return virtio_blk_process_request(vdev, bvsession->io_channel, &user_task->blk_task, + vhost_user_blk_request_finish, NULL); } static struct spdk_vhost_blk_dev * @@ -189,7 +181,7 @@ blk_task_enqueue(struct spdk_vhost_user_blk_task *task) } static void -vhost_user_blk_request_finish(uint8_t status, struct spdk_vhost_blk_task *task) +vhost_user_blk_request_finish(uint8_t status, struct spdk_vhost_blk_task *task, void *cb_arg) { struct spdk_vhost_user_blk_task *user_task; @@ -210,7 +202,7 @@ blk_request_finish(uint8_t status, struct spdk_vhost_blk_task *task) *task->status = status; } - vhost_user_blk_request_finish(status, task); + task->cb(status, task, task->cb_arg); } /* @@ -457,7 +449,8 @@ blk_request_resubmit(void *arg) struct spdk_vhost_blk_task *task = arg; int rc = 0; - rc = virtio_blk_process_request(task->bdev_io_wait_vdev, task->bdev_io_wait_ch, task); + rc = virtio_blk_process_request(task->bdev_io_wait_vdev, task->bdev_io_wait_ch, task, + task->cb, task->cb_arg); if (rc == 0) { SPDK_DEBUGLOG(vhost_blk, "====== Task %p resubmitted ======\n", task); } else { @@ -484,9 +477,9 @@ blk_request_queue_io(struct spdk_vhost_dev *vdev, struct spdk_io_channel *ch, } } -static int +int virtio_blk_process_request(struct spdk_vhost_dev *vdev, struct spdk_io_channel *ch, - struct spdk_vhost_blk_task *task) + struct spdk_vhost_blk_task *task, virtio_blk_request_cb cb, void *cb_arg) { struct spdk_vhost_blk_dev *bvdev = to_blk_dev(vdev); struct virtio_blk_outhdr req; @@ -498,6 +491,9 @@ virtio_blk_process_request(struct spdk_vhost_dev *vdev, struct spdk_io_channel * uint16_t iovcnt; int rc; + task->cb = cb; + task->cb_arg = cb_arg; + iov = &task->iovs[0]; if (spdk_unlikely(iov->iov_len != sizeof(req))) { SPDK_DEBUGLOG(vhost_blk, @@ -693,7 +689,7 @@ process_blk_task(struct spdk_vhost_virtqueue *vq, uint16_t req_idx) if (rc) { SPDK_DEBUGLOG(vhost_blk, "Invalid request (req_idx = %"PRIu16").\n", task->req_idx); /* Only READ and WRITE are supported for now. */ - vhost_user_blk_request_finish(VIRTIO_BLK_S_UNSUPP, blk_task); + vhost_user_blk_request_finish(VIRTIO_BLK_S_UNSUPP, blk_task, NULL); return; } @@ -758,7 +754,7 @@ process_packed_blk_task(struct spdk_vhost_virtqueue *vq, uint16_t req_idx) if (rc) { SPDK_DEBUGLOG(vhost_blk, "Invalid request (req_idx = %"PRIu16").\n", task->req_idx); /* Only READ and WRITE are supported for now. */ - vhost_user_blk_request_finish(VIRTIO_BLK_S_UNSUPP, blk_task); + vhost_user_blk_request_finish(VIRTIO_BLK_S_UNSUPP, blk_task, NULL); return; } @@ -819,7 +815,7 @@ process_packed_inflight_blk_task(struct spdk_vhost_virtqueue *vq, if (rc) { SPDK_DEBUGLOG(vhost_blk, "Invalid request (req_idx = %"PRIu16").\n", task->req_idx); /* Only READ and WRITE are supported for now. */ - vhost_user_blk_request_finish(VIRTIO_BLK_S_UNSUPP, blk_task); + vhost_user_blk_request_finish(VIRTIO_BLK_S_UNSUPP, blk_task, NULL); return; } @@ -1134,8 +1130,6 @@ vhost_blk_poller_set_interrupt_mode(struct spdk_poller *poller, void *cb_arg, bo vhost_user_session_set_interrupt_mode(&bvsession->vsession, interrupt_mode); } -typedef void (*bdev_event_cb_complete)(struct spdk_vhost_dev *vdev, void *ctx); - static void bdev_event_cpl_cb(struct spdk_vhost_dev *vdev, void *ctx) { @@ -1240,6 +1234,7 @@ bdev_event_cb(enum spdk_bdev_event_type type, struct spdk_bdev *bdev, void *event_ctx) { struct spdk_vhost_dev *vdev = (struct spdk_vhost_dev *)event_ctx; + struct spdk_vhost_blk_dev *bvdev = to_blk_dev(vdev); SPDK_DEBUGLOG(vhost_blk, "Bdev event: type %d, name %s\n", type, @@ -1248,7 +1243,7 @@ bdev_event_cb(enum spdk_bdev_event_type type, struct spdk_bdev *bdev, switch (type) { case SPDK_BDEV_EVENT_REMOVE: case SPDK_BDEV_EVENT_RESIZE: - vhost_user_bdev_event_cb(type, vdev, bdev_event_cpl_cb, (void *)type); + bvdev->ops->bdev_event(type, vdev, bdev_event_cpl_cb, (void *)type); break; default: SPDK_NOTICELOG("Unsupported bdev event: type %d\n", type); @@ -1484,6 +1479,7 @@ vhost_blk_dump_info_json(struct spdk_vhost_dev *vdev, struct spdk_json_write_ctx } else { spdk_json_write_null(w); } + spdk_json_write_named_string(w, "transport", bvdev->ops->name); spdk_json_write_object_end(w); } @@ -1509,6 +1505,7 @@ vhost_blk_write_config_json(struct spdk_vhost_dev *vdev, struct spdk_json_write_ spdk_json_write_named_string(w, "cpumask", spdk_cpuset_fmt(spdk_thread_get_cpumask(vdev->thread))); spdk_json_write_named_bool(w, "readonly", bvdev->readonly); + spdk_json_write_named_string(w, "transport", bvdev->ops->name); spdk_json_write_object_end(w); spdk_json_write_object_end(w); @@ -1593,31 +1590,44 @@ static const struct spdk_vhost_dev_backend vhost_blk_device_backend = { }; int -spdk_vhost_blk_construct(const char *name, const char *cpumask, const char *dev_name, - const struct spdk_json_val *params) +virtio_blk_construct_ctrlr(struct spdk_vhost_dev *vdev, const char *address, + struct spdk_cpuset *cpumask, const struct spdk_json_val *params, + const struct spdk_vhost_user_dev_backend *user_backend) +{ + struct spdk_vhost_blk_dev *bvdev = to_blk_dev(vdev); + + return bvdev->ops->create_ctrlr(vdev, cpumask, address, params, (void *)user_backend); +} + +int +spdk_vhost_blk_construct(const char *name, const char *cpumask, const char *dev_name, + const char *transport, const struct spdk_json_val *params) { - struct rpc_vhost_blk req = {0}; struct spdk_vhost_blk_dev *bvdev = NULL; struct spdk_vhost_dev *vdev; struct spdk_bdev *bdev; + const char *transport_name = VIRTIO_BLK_DEFAULT_TRANSPORT; int ret = 0; spdk_vhost_lock(); - if (spdk_json_decode_object_relaxed(params, rpc_construct_vhost_blk, - SPDK_COUNTOF(rpc_construct_vhost_blk), - &req)) { - SPDK_DEBUGLOG(vhost_blk, "spdk_json_decode_object failed\n"); - ret = -EINVAL; - goto out; - } - bvdev = calloc(1, sizeof(*bvdev)); if (bvdev == NULL) { ret = -ENOMEM; goto out; } + if (transport != NULL) { + transport_name = transport; + } + + bvdev->ops = virtio_blk_get_transport_ops(transport_name); + if (!bvdev->ops) { + ret = -EINVAL; + SPDK_ERRLOG("Transport type '%s' unavailable.\n", transport_name); + goto out; + } + ret = spdk_bdev_open_ext(dev_name, true, bdev_event_cb, bvdev, &bvdev->bdev_desc); if (ret != 0) { SPDK_ERRLOG("%s: could not open bdev '%s', error=%d\n", @@ -1630,12 +1640,6 @@ spdk_vhost_blk_construct(const char *name, const char *cpumask, const char *dev_ vdev->virtio_features = SPDK_VHOST_BLK_FEATURES_BASE; vdev->disabled_features = SPDK_VHOST_BLK_DISABLED_FEATURES; vdev->protocol_features = SPDK_VHOST_BLK_PROTOCOL_FEATURES; - vdev->packed_ring_recovery = false; - - if (req.packed_ring) { - vdev->virtio_features |= (uint64_t)req.packed_ring << VIRTIO_F_RING_PACKED; - vdev->packed_ring_recovery = req.packed_ring_recovery; - } if (spdk_bdev_io_type_supported(bdev, SPDK_BDEV_IO_TYPE_UNMAP)) { vdev->virtio_features |= (1ULL << VIRTIO_BLK_F_DISCARD); @@ -1643,9 +1647,7 @@ spdk_vhost_blk_construct(const char *name, const char *cpumask, const char *dev_ if (spdk_bdev_io_type_supported(bdev, SPDK_BDEV_IO_TYPE_WRITE_ZEROES)) { vdev->virtio_features |= (1ULL << VIRTIO_BLK_F_WRITE_ZEROES); } - if (req.readonly) { - vdev->virtio_features |= (1ULL << VIRTIO_BLK_F_RO); - } + if (spdk_bdev_io_type_supported(bdev, SPDK_BDEV_IO_TYPE_FLUSH)) { vdev->virtio_features |= (1ULL << VIRTIO_BLK_F_FLUSH); } @@ -1663,8 +1665,8 @@ spdk_vhost_blk_construct(const char *name, const char *cpumask, const char *dev_ bvdev->dummy_io_channel = spdk_bdev_get_io_channel(bvdev->bdev_desc); bvdev->bdev = bdev; - bvdev->readonly = req.readonly; - ret = vhost_dev_register(vdev, name, cpumask, &vhost_blk_device_backend, + bvdev->readonly = false; + ret = vhost_dev_register(vdev, name, cpumask, params, &vhost_blk_device_backend, &vhost_blk_user_device_backend); if (ret != 0) { spdk_put_io_channel(bvdev->dummy_io_channel); @@ -1681,6 +1683,14 @@ out: return ret; } +int +virtio_blk_destroy_ctrlr(struct spdk_vhost_dev *vdev) +{ + struct spdk_vhost_blk_dev *bvdev = to_blk_dev(vdev); + + return bvdev->ops->destroy_ctrlr(vdev); +} + static int vhost_blk_destroy(struct spdk_vhost_dev *vdev) { @@ -1723,5 +1733,96 @@ vhost_blk_put_io_channel(struct spdk_io_channel *ch) spdk_put_io_channel(ch); } +static struct spdk_virtio_blk_transport * +vhost_user_blk_create(const struct spdk_json_val *params) +{ + int ret; + struct spdk_virtio_blk_transport *vhost_user_blk; + + vhost_user_blk = calloc(1, sizeof(*vhost_user_blk)); + if (!vhost_user_blk) { + return NULL; + } + + ret = vhost_user_init(); + if (ret != 0) { + free(vhost_user_blk); + return NULL; + } + + return vhost_user_blk; +} + +static int +vhost_user_blk_destroy(struct spdk_virtio_blk_transport *transport, + spdk_vhost_fini_cb cb_fn) +{ + vhost_user_fini(cb_fn); + free(transport); + return 0; +} + +struct rpc_vhost_blk { + bool readonly; + bool packed_ring; + bool packed_ring_recovery; +}; + +static const struct spdk_json_object_decoder rpc_construct_vhost_blk[] = { + {"readonly", offsetof(struct rpc_vhost_blk, readonly), spdk_json_decode_bool, true}, + {"packed_ring", offsetof(struct rpc_vhost_blk, packed_ring), spdk_json_decode_bool, true}, + {"packed_ring_recovery", offsetof(struct rpc_vhost_blk, packed_ring_recovery), spdk_json_decode_bool, true}, +}; + +static int +vhost_user_blk_create_ctrlr(struct spdk_vhost_dev *vdev, struct spdk_cpuset *cpumask, + const char *address, const struct spdk_json_val *params, void *custom_opts) +{ + struct rpc_vhost_blk req = {0}; + struct spdk_vhost_blk_dev *bvdev = to_blk_dev(vdev); + + if (spdk_json_decode_object_relaxed(params, rpc_construct_vhost_blk, + SPDK_COUNTOF(rpc_construct_vhost_blk), + &req)) { + SPDK_DEBUGLOG(vhost_blk, "spdk_json_decode_object failed\n"); + return -EINVAL; + } + + vdev->packed_ring_recovery = false; + + if (req.packed_ring) { + vdev->virtio_features |= (uint64_t)req.packed_ring << VIRTIO_F_RING_PACKED; + vdev->packed_ring_recovery = req.packed_ring_recovery; + } + if (req.readonly) { + vdev->virtio_features |= (1ULL << VIRTIO_BLK_F_RO); + bvdev->readonly = req.readonly; + } + + return vhost_user_dev_register(vdev, address, cpumask, custom_opts); +} + +static int +vhost_user_blk_destroy_ctrlr(struct spdk_vhost_dev *vdev) +{ + return vhost_user_dev_unregister(vdev); +} + +static const struct spdk_virtio_blk_transport_ops vhost_user_blk = { + .name = "vhost_user_blk", + + .dump_opts = NULL, + + .create = vhost_user_blk_create, + .destroy = vhost_user_blk_destroy, + + .create_ctrlr = vhost_user_blk_create_ctrlr, + .destroy_ctrlr = vhost_user_blk_destroy_ctrlr, + + .bdev_event = vhost_user_bdev_event_cb, +}; + +SPDK_VIRTIO_BLK_TRANSPORT_REGISTER(vhost_user_blk, &vhost_user_blk); + SPDK_LOG_REGISTER_COMPONENT(vhost_blk) SPDK_LOG_REGISTER_COMPONENT(vhost_blk_data) diff --git a/lib/vhost/vhost_internal.h b/lib/vhost/vhost_internal.h index b26304b61..d213295a1 100644 --- a/lib/vhost/vhost_internal.h +++ b/lib/vhost/vhost_internal.h @@ -424,6 +424,7 @@ vhost_dev_has_feature(struct spdk_vhost_session *vsession, unsigned feature_id) } int vhost_dev_register(struct spdk_vhost_dev *vdev, const char *name, const char *mask_str, + const struct spdk_json_val *params, const struct spdk_vhost_dev_backend *backend, const struct spdk_vhost_user_dev_backend *user_backend); int vhost_dev_unregister(struct spdk_vhost_dev *vdev); @@ -537,8 +538,20 @@ int vhost_user_dev_unregister(struct spdk_vhost_dev *vdev); int vhost_user_init(void); void vhost_user_fini(spdk_vhost_fini_cb vhost_cb); +int virtio_blk_construct_ctrlr(struct spdk_vhost_dev *vdev, const char *address, + struct spdk_cpuset *cpumask, const struct spdk_json_val *params, + const struct spdk_vhost_user_dev_backend *user_backend); +int virtio_blk_destroy_ctrlr(struct spdk_vhost_dev *vdev); + +struct spdk_vhost_blk_task; + +typedef void (*virtio_blk_request_cb)(uint8_t status, struct spdk_vhost_blk_task *task, + void *cb_arg); + struct spdk_vhost_blk_task { struct spdk_bdev_io *bdev_io; + virtio_blk_request_cb cb; + void *cb_arg; volatile uint8_t *status; @@ -556,4 +569,80 @@ struct spdk_vhost_blk_task { uint32_t payload_size; }; +int virtio_blk_process_request(struct spdk_vhost_dev *vdev, struct spdk_io_channel *ch, + struct spdk_vhost_blk_task *task, virtio_blk_request_cb cb, void *cb_arg); + +typedef void (*bdev_event_cb_complete)(struct spdk_vhost_dev *vdev, void *ctx); + +#define SPDK_VIRTIO_BLK_TRSTRING_MAX_LEN 32 + +struct spdk_virtio_blk_transport_ops { + /** + * Transport name + */ + char name[SPDK_VIRTIO_BLK_TRSTRING_MAX_LEN]; + + /** + * Create a transport for the given transport opts + */ + struct spdk_virtio_blk_transport *(*create)(const struct spdk_json_val *params); + + /** + * Dump transport-specific opts into JSON + */ + void (*dump_opts)(struct spdk_virtio_blk_transport *transport, struct spdk_json_write_ctx *w); + + /** + * Destroy the transport + */ + int (*destroy)(struct spdk_virtio_blk_transport *transport, + spdk_vhost_fini_cb cb_fn); + + /** + * Create vhost block controller + */ + int (*create_ctrlr)(struct spdk_vhost_dev *vdev, struct spdk_cpuset *cpumask, + const char *address, const struct spdk_json_val *params, + void *custom_opts); + + /** + * Destroy vhost block controller + */ + int (*destroy_ctrlr)(struct spdk_vhost_dev *vdev); + + /* + * Signal removal of the bdev. + */ + void (*bdev_event)(enum spdk_bdev_event_type type, struct spdk_vhost_dev *vdev, + bdev_event_cb_complete cb, void *cb_arg); +}; + +struct spdk_virtio_blk_transport { + const struct spdk_virtio_blk_transport_ops *ops; + TAILQ_ENTRY(spdk_virtio_blk_transport) tailq; +}; + +struct virtio_blk_transport_ops_list_element { + struct spdk_virtio_blk_transport_ops ops; + TAILQ_ENTRY(virtio_blk_transport_ops_list_element) link; +}; + +void virtio_blk_transport_register(const struct spdk_virtio_blk_transport_ops *ops); +int virtio_blk_transport_create(const char *transport_name, const struct spdk_json_val *params); +int virtio_blk_transport_destroy(struct spdk_virtio_blk_transport *transport, + spdk_vhost_fini_cb cb_fn); + +const struct spdk_virtio_blk_transport_ops * +virtio_blk_get_transport_ops(const char *transport_name); + + +/* + * Macro used to register new transports. + */ +#define SPDK_VIRTIO_BLK_TRANSPORT_REGISTER(name, transport_ops) \ +static void __attribute__((constructor)) _virtio_blk_transport_register_##name(void) \ +{ \ + virtio_blk_transport_register(transport_ops); \ +}\ + #endif /* SPDK_VHOST_INTERNAL_H */ diff --git a/lib/vhost/vhost_rpc.c b/lib/vhost/vhost_rpc.c index 59a2a0cd5..b90ccff45 100644 --- a/lib/vhost/vhost_rpc.c +++ b/lib/vhost/vhost_rpc.c @@ -232,12 +232,14 @@ struct rpc_vhost_blk_ctrlr { char *ctrlr; char *dev_name; char *cpumask; + char *transport; }; static const struct spdk_json_object_decoder rpc_construct_vhost_blk_ctrlr[] = { {"ctrlr", offsetof(struct rpc_vhost_blk_ctrlr, ctrlr), spdk_json_decode_string }, {"dev_name", offsetof(struct rpc_vhost_blk_ctrlr, dev_name), spdk_json_decode_string }, {"cpumask", offsetof(struct rpc_vhost_blk_ctrlr, cpumask), spdk_json_decode_string, true}, + {"transport", offsetof(struct rpc_vhost_blk_ctrlr, transport), spdk_json_decode_string, true}, }; static void @@ -246,6 +248,7 @@ free_rpc_vhost_blk_ctrlr(struct rpc_vhost_blk_ctrlr *req) free(req->ctrlr); free(req->dev_name); free(req->cpumask); + free(req->transport); } static void @@ -263,7 +266,7 @@ rpc_vhost_create_blk_controller(struct spdk_jsonrpc_request *request, goto invalid; } - rc = spdk_vhost_blk_construct(req.ctrlr, req.cpumask, req.dev_name, params); + rc = spdk_vhost_blk_construct(req.ctrlr, req.cpumask, req.dev_name, req.transport, params); if (rc < 0) { goto invalid; } @@ -497,4 +500,51 @@ invalid: SPDK_RPC_REGISTER("vhost_controller_set_coalescing", rpc_vhost_controller_set_coalescing, SPDK_RPC_RUNTIME) +struct rpc_virtio_blk_create_transport { + char *name; +}; + +static const struct spdk_json_object_decoder rpc_create_virtio_blk_transport[] = { + {"name", offsetof(struct rpc_virtio_blk_create_transport, name), spdk_json_decode_string}, +}; + +static void +free_rpc_virtio_blk_create_transport(struct rpc_virtio_blk_create_transport *req) +{ + free(req->name); +} + +static void +rpc_virtio_blk_create_transport(struct spdk_jsonrpc_request *request, + const struct spdk_json_val *params) +{ + struct rpc_virtio_blk_create_transport req = {0}; + int rc; + + if (spdk_json_decode_object_relaxed(params, rpc_create_virtio_blk_transport, + SPDK_COUNTOF(rpc_create_virtio_blk_transport), &req)) { + SPDK_DEBUGLOG(vhost_rpc, "spdk_json_decode_object failed\n"); + rc = -EINVAL; + goto invalid; + } + + spdk_vhost_lock(); + rc = virtio_blk_transport_create(req.name, params); + spdk_vhost_unlock(); + if (rc != 0) { + goto invalid; + } + + free_rpc_virtio_blk_create_transport(&req); + spdk_jsonrpc_send_bool_response(request, true); + return; + +invalid: + free_rpc_virtio_blk_create_transport(&req); + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, + spdk_strerror(-rc)); +} +SPDK_RPC_REGISTER("virtio_blk_create_transport", rpc_virtio_blk_create_transport, + SPDK_RPC_RUNTIME) + SPDK_LOG_REGISTER_COMPONENT(vhost_rpc) diff --git a/lib/vhost/vhost_scsi.c b/lib/vhost/vhost_scsi.c index ac961568e..88b19ccc5 100644 --- a/lib/vhost/vhost_scsi.c +++ b/lib/vhost/vhost_scsi.c @@ -882,7 +882,7 @@ spdk_vhost_scsi_dev_construct(const char *name, const char *cpumask) svdev->vdev.protocol_features = SPDK_VHOST_SCSI_PROTOCOL_FEATURES; spdk_vhost_lock(); - rc = vhost_dev_register(&svdev->vdev, name, cpumask, + rc = vhost_dev_register(&svdev->vdev, name, cpumask, NULL, &spdk_vhost_scsi_device_backend, &spdk_vhost_scsi_user_device_backend); if (rc) { diff --git a/python/spdk/rpc/vhost.py b/python/spdk/rpc/vhost.py index 280ba3a24..24b986ae4 100644 --- a/python/spdk/rpc/vhost.py +++ b/python/spdk/rpc/vhost.py @@ -16,6 +16,15 @@ def vhost_controller_set_coalescing(client, ctrlr, delay_base_us, iops_threshold return client.call('vhost_controller_set_coalescing', params) +def virtio_blk_create_transport(client, name, opts): + """Create virtio blk transport. + Args: + name: transport name + """ + params = {'name': name} + return client.call('virtio_blk_create_transport', params) + + def vhost_create_scsi_controller(client, ctrlr, cpumask=None): """Create a vhost scsi controller. Args: @@ -58,12 +67,14 @@ def vhost_scsi_controller_remove_target(client, ctrlr, scsi_target_num): return client.call('vhost_scsi_controller_remove_target', params) -def vhost_create_blk_controller(client, ctrlr, dev_name, cpumask=None, readonly=None, packed_ring=None, packed_ring_recovery=None): +def vhost_create_blk_controller( + client, ctrlr, dev_name, cpumask=None, transport=None, readonly=None, packed_ring=None, packed_ring_recovery=None): """Create vhost BLK controller. Args: ctrlr: controller name dev_name: device name to add to controller cpumask: cpu mask for this controller + transport: virtio blk transport name (default: vhost_user_blk) readonly: set controller as read-only packed_ring: support controller packed_ring packed_ring_recovery: enable packed ring live recovery @@ -74,6 +85,8 @@ def vhost_create_blk_controller(client, ctrlr, dev_name, cpumask=None, readonly= } if cpumask: params['cpumask'] = cpumask + if transport: + params['transport'] = transport if readonly: params['readonly'] = readonly if packed_ring: diff --git a/scripts/rpc.py b/scripts/rpc.py index d91a12e64..9d8c5699c 100755 --- a/scripts/rpc.py +++ b/scripts/rpc.py @@ -2417,6 +2417,16 @@ Format: 'user:u1 secret:s1 muser:mu1 msecret:ms1,user:u2 secret:s2 muser:mu2 mse p.add_argument('iops_threshold', help='IOPS threshold when coalescing is enabled', type=int) p.set_defaults(func=vhost_controller_set_coalescing) + def virtio_blk_create_transport(args): + rpc.vhost.virtio_blk_create_transport(args.client, + name=args.name, + opts=args.opts) + + p = subparsers.add_parser('virtio_blk_create_transport', + help='Create virtio blk transport') + p.add_argument('name', help='transport name') + p.set_defaults(func=virtio_blk_create_transport) + def vhost_create_scsi_controller(args): rpc.vhost.vhost_create_scsi_controller(args.client, ctrlr=args.ctrlr, @@ -2455,6 +2465,7 @@ Format: 'user:u1 secret:s1 muser:mu1 msecret:ms1,user:u2 secret:s2 muser:mu2 mse ctrlr=args.ctrlr, dev_name=args.dev_name, cpumask=args.cpumask, + transport=args.transport, readonly=args.readonly, packed_ring=args.packed_ring, packed_ring_recovery=args.packed_ring_recovery) @@ -2463,6 +2474,7 @@ Format: 'user:u1 secret:s1 muser:mu1 msecret:ms1,user:u2 secret:s2 muser:mu2 mse p.add_argument('ctrlr', help='controller name') p.add_argument('dev_name', help='device name') p.add_argument('--cpumask', help='cpu mask for this controller') + p.add_argument('--transport', help='virtio blk transport name (default: vhost_user_blk)') p.add_argument("-r", "--readonly", action='store_true', help='Set controller as read-only') p.add_argument("-p", "--packed_ring", action='store_true', help='Set controller as packed ring supported') p.add_argument("-l", "--packed_ring_recovery", action='store_true', help='Enable packed ring live recovery') diff --git a/test/unit/lib/vhost/vhost.c/vhost_ut.c b/test/unit/lib/vhost/vhost.c/vhost_ut.c index ba9946ba8..6ba31bb92 100644 --- a/test/unit/lib/vhost/vhost.c/vhost_ut.c +++ b/test/unit/lib/vhost/vhost.c/vhost_ut.c @@ -232,7 +232,7 @@ alloc_vdev(struct spdk_vhost_dev **vdev_p, const char *name, const char *cpumask CU_ASSERT(rc == 0); SPDK_CU_ASSERT_FATAL(vdev != NULL); memset(vdev, 0, sizeof(*vdev)); - rc = vhost_dev_register(vdev, name, cpumask, &g_vdev_backend, &g_vdev_user_backend); + rc = vhost_dev_register(vdev, name, cpumask, NULL, &g_vdev_backend, &g_vdev_user_backend); if (rc == 0) { *vdev_p = vdev; } else {