vhost_user_nvme: add vhost user nvme target to SPDK
Similar with exist vhost scsi/blk target, this commit introduces a new target: vhost nvme I/O slave target, QEMU will present an emulated NVMe controller to VM, the SPDK I/O slave target will process the I/Os sent from Guest VM. Users can follow the example configuation file to evaluate this feature, refer to etc/spdk/vhost.conf.in [VhostNvme]. Change-Id: Ia2a8a3f719573f3268177234812bd28ed0082d5c Signed-off-by: Changpeng Liu <changpeng.liu@intel.com> Reviewed-on: https://review.gerrithub.io/384213 Tested-by: SPDK Automated Test System <sys_sgsw@intel.com> Reviewed-by: Daniel Verkamp <daniel.verkamp@intel.com> Reviewed-by: Jim Harris <james.r.harris@intel.com>
This commit is contained in:
parent
20e69cf6b1
commit
90c0e24410
@ -138,3 +138,17 @@
|
|||||||
# this cpumask. By default, it not specified, will use any core in the
|
# this cpumask. By default, it not specified, will use any core in the
|
||||||
# SPDK process.
|
# SPDK process.
|
||||||
#Cpumask 0x1
|
#Cpumask 0x1
|
||||||
|
|
||||||
|
#[VhostNvme0]
|
||||||
|
# Define name for controller
|
||||||
|
#Name vhost.0
|
||||||
|
#NumberOfQueues 2
|
||||||
|
# Use first partition from the first NVMe device
|
||||||
|
#Namespace Nvme0n1p0
|
||||||
|
# Use first partition from the first NVMe device
|
||||||
|
#Namespace Nvme0n1p1
|
||||||
|
|
||||||
|
# Start the poller for this vhost controller on one of the cores in
|
||||||
|
# this cpumask. By default, it not specified, will use any core in the
|
||||||
|
# SPDK process.
|
||||||
|
#Cpumask 0x1
|
||||||
|
@ -38,7 +38,7 @@ CFLAGS += -I.
|
|||||||
CFLAGS += -Irte_vhost
|
CFLAGS += -Irte_vhost
|
||||||
CFLAGS += $(ENV_CFLAGS)
|
CFLAGS += $(ENV_CFLAGS)
|
||||||
|
|
||||||
C_SRCS = vhost.c vhost_rpc.c vhost_scsi.c vhost_blk.c
|
C_SRCS = vhost.c vhost_rpc.c vhost_scsi.c vhost_blk.c vhost_nvme.c
|
||||||
|
|
||||||
LIBNAME = vhost
|
LIBNAME = vhost
|
||||||
|
|
||||||
|
@ -104,6 +104,9 @@ struct vhost_device_ops {
|
|||||||
* is used to inform the application on such change.
|
* is used to inform the application on such change.
|
||||||
*/
|
*/
|
||||||
int (*features_changed)(int vid, uint64_t features);
|
int (*features_changed)(int vid, uint64_t features);
|
||||||
|
int (*vhost_nvme_admin_passthrough)(int vid, void *cmd, void *cqe, void *buf);
|
||||||
|
int (*vhost_nvme_set_cq_call)(int vid, uint16_t qid, int fd);
|
||||||
|
int (*vhost_nvme_get_cap)(int vid, uint64_t *cap);
|
||||||
|
|
||||||
int (*new_connection)(int vid);
|
int (*new_connection)(int vid);
|
||||||
void (*destroy_connection)(int vid);
|
void (*destroy_connection)(int vid);
|
||||||
|
@ -178,6 +178,7 @@ struct virtio_net {
|
|||||||
uint64_t negotiated_features;
|
uint64_t negotiated_features;
|
||||||
uint64_t protocol_features;
|
uint64_t protocol_features;
|
||||||
int vid;
|
int vid;
|
||||||
|
uint32_t is_nvme;
|
||||||
uint32_t flags;
|
uint32_t flags;
|
||||||
uint16_t vhost_hlen;
|
uint16_t vhost_hlen;
|
||||||
/* to tell if we need broadcast rarp packet */
|
/* to tell if we need broadcast rarp packet */
|
||||||
|
@ -35,6 +35,7 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <stdbool.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
@ -78,6 +79,11 @@ static const char *vhost_message_str[VHOST_USER_MAX] = {
|
|||||||
[VHOST_USER_NET_SET_MTU] = "VHOST_USER_NET_SET_MTU",
|
[VHOST_USER_NET_SET_MTU] = "VHOST_USER_NET_SET_MTU",
|
||||||
[VHOST_USER_GET_CONFIG] = "VHOST_USER_GET_CONFIG",
|
[VHOST_USER_GET_CONFIG] = "VHOST_USER_GET_CONFIG",
|
||||||
[VHOST_USER_SET_CONFIG] = "VHOST_USER_SET_CONFIG",
|
[VHOST_USER_SET_CONFIG] = "VHOST_USER_SET_CONFIG",
|
||||||
|
[VHOST_USER_NVME_ADMIN] = "VHOST_USER_NVME_ADMIN",
|
||||||
|
[VHOST_USER_NVME_SET_CQ_CALL] = "VHOST_USER_NVME_SET_CQ_CALL",
|
||||||
|
[VHOST_USER_NVME_GET_CAP] = "VHOST_USER_NVME_GET_CAP",
|
||||||
|
[VHOST_USER_NVME_START_STOP] = "VHOST_USER_NVME_START_STOP",
|
||||||
|
[VHOST_USER_NVME_IO_CMD] = "VHOST_USER_NVME_IO_CMD"
|
||||||
};
|
};
|
||||||
|
|
||||||
static uint64_t
|
static uint64_t
|
||||||
@ -548,6 +554,14 @@ vhost_user_set_mem_table(struct virtio_net *dev, struct VhostUserMsg *pmsg)
|
|||||||
memcpy(&dev->mem_table, &pmsg->payload.memory, sizeof(dev->mem_table));
|
memcpy(&dev->mem_table, &pmsg->payload.memory, sizeof(dev->mem_table));
|
||||||
memcpy(dev->mem_table_fds, pmsg->fds, sizeof(dev->mem_table_fds));
|
memcpy(dev->mem_table_fds, pmsg->fds, sizeof(dev->mem_table_fds));
|
||||||
dev->has_new_mem_table = 1;
|
dev->has_new_mem_table = 1;
|
||||||
|
/* vhost-user-nvme will not send
|
||||||
|
* set vring addr message, enable
|
||||||
|
* memory address table now.
|
||||||
|
*/
|
||||||
|
if (dev->has_new_mem_table && dev->is_nvme) {
|
||||||
|
vhost_setup_mem_table(dev);
|
||||||
|
dev->has_new_mem_table = 0;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -1040,12 +1054,59 @@ vhost_user_check_and_alloc_queue_pair(struct virtio_net *dev, VhostUserMsg *msg)
|
|||||||
return alloc_vring_queue(dev, vring_idx);
|
return alloc_vring_queue(dev, vring_idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
vhost_user_nvme_io_request_passthrough(struct virtio_net *dev,
|
||||||
|
uint16_t qid, uint16_t tail_head,
|
||||||
|
bool is_submission_queue)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
vhost_user_nvme_admin_passthrough(struct virtio_net *dev,
|
||||||
|
void *cmd, void *cqe, void *buf)
|
||||||
|
{
|
||||||
|
if (dev->notify_ops->vhost_nvme_admin_passthrough) {
|
||||||
|
return dev->notify_ops->vhost_nvme_admin_passthrough(dev->vid, cmd, cqe, buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
vhost_user_nvme_set_cq_call(struct virtio_net *dev, uint16_t qid, int fd)
|
||||||
|
{
|
||||||
|
if (dev->notify_ops->vhost_nvme_set_cq_call) {
|
||||||
|
return dev->notify_ops->vhost_nvme_set_cq_call(dev->vid, qid, fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
vhost_user_nvme_get_cap(struct virtio_net *dev, uint64_t *cap)
|
||||||
|
{
|
||||||
|
if (dev->notify_ops->vhost_nvme_get_cap) {
|
||||||
|
return dev->notify_ops->vhost_nvme_get_cap(dev->vid, cap);
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
vhost_user_msg_handler(int vid, int fd)
|
vhost_user_msg_handler(int vid, int fd)
|
||||||
{
|
{
|
||||||
struct virtio_net *dev;
|
struct virtio_net *dev;
|
||||||
struct VhostUserMsg msg;
|
struct VhostUserMsg msg;
|
||||||
|
struct vhost_vring_file file;
|
||||||
int ret;
|
int ret;
|
||||||
|
uint64_t cap;
|
||||||
|
uint64_t enable;
|
||||||
|
uint8_t cqe[16];
|
||||||
|
uint8_t cmd[64];
|
||||||
|
uint8_t buf[4096];
|
||||||
|
uint16_t qid, tail_head;
|
||||||
|
bool is_submission_queue;
|
||||||
|
|
||||||
dev = get_device(vid);
|
dev = get_device(vid);
|
||||||
if (dev == NULL)
|
if (dev == NULL)
|
||||||
@ -1106,6 +1167,60 @@ vhost_user_msg_handler(int vid, int fd)
|
|||||||
ret = 0;
|
ret = 0;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case VHOST_USER_NVME_ADMIN:
|
||||||
|
if (!dev->is_nvme) {
|
||||||
|
dev->is_nvme = 1;
|
||||||
|
}
|
||||||
|
memcpy(cmd, &msg.payload.nvme.cmd, 64);
|
||||||
|
ret = vhost_user_nvme_admin_passthrough(dev, cmd, cqe, buf);
|
||||||
|
memcpy(&msg.payload.nvme.cmd, &cqe, 16);
|
||||||
|
msg.size = 16;
|
||||||
|
/* NVMe Identify Command */
|
||||||
|
if (cmd[0] == 0x06) {
|
||||||
|
memcpy(msg.payload.nvme.buf, &buf, 4096);
|
||||||
|
msg.size += 4096;
|
||||||
|
} else if (cmd[0] == 0x09 || cmd[0] == 0x0a) {
|
||||||
|
memcpy(&msg.payload.nvme.buf, &buf, 4);
|
||||||
|
msg.size += 4096;
|
||||||
|
|
||||||
|
}
|
||||||
|
send_vhost_message(fd, &msg);
|
||||||
|
break;
|
||||||
|
case VHOST_USER_NVME_SET_CQ_CALL:
|
||||||
|
file.index = msg.payload.u64 & VHOST_USER_VRING_IDX_MASK;
|
||||||
|
file.fd = msg.fds[0];
|
||||||
|
ret = vhost_user_nvme_set_cq_call(dev, file.index, file.fd);
|
||||||
|
break;
|
||||||
|
case VHOST_USER_NVME_GET_CAP:
|
||||||
|
ret = vhost_user_nvme_get_cap(dev, &cap);
|
||||||
|
if (!ret)
|
||||||
|
msg.payload.u64 = cap;
|
||||||
|
else
|
||||||
|
msg.payload.u64 = 0;
|
||||||
|
msg.size = sizeof(msg.payload.u64);
|
||||||
|
send_vhost_message(fd, &msg);
|
||||||
|
break;
|
||||||
|
case VHOST_USER_NVME_START_STOP:
|
||||||
|
enable = msg.payload.u64;
|
||||||
|
/* device must be started before set cq call */
|
||||||
|
if (enable) {
|
||||||
|
if (!(dev->flags & VIRTIO_DEV_RUNNING)) {
|
||||||
|
if (dev->notify_ops->new_device(dev->vid) == 0)
|
||||||
|
dev->flags |= VIRTIO_DEV_RUNNING;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (dev->flags & VIRTIO_DEV_RUNNING) {
|
||||||
|
dev->flags &= ~VIRTIO_DEV_RUNNING;
|
||||||
|
dev->notify_ops->destroy_device(dev->vid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case VHOST_USER_NVME_IO_CMD:
|
||||||
|
qid = msg.payload.nvme_io.qid;
|
||||||
|
tail_head = msg.payload.nvme_io.tail_head;
|
||||||
|
is_submission_queue = (msg.payload.nvme_io.queue_type == VHOST_USER_NVME_SUBMISSION_QUEUE) ? true : false;
|
||||||
|
vhost_user_nvme_io_request_passthrough(dev, qid, tail_head, is_submission_queue);
|
||||||
|
break;
|
||||||
case VHOST_USER_GET_FEATURES:
|
case VHOST_USER_GET_FEATURES:
|
||||||
msg.payload.u64 = vhost_user_get_features(dev);
|
msg.payload.u64 = vhost_user_get_features(dev);
|
||||||
msg.size = sizeof(msg.payload.u64);
|
msg.size = sizeof(msg.payload.u64);
|
||||||
|
@ -84,6 +84,11 @@ typedef enum VhostUserRequest {
|
|||||||
VHOST_USER_NET_SET_MTU = 20,
|
VHOST_USER_NET_SET_MTU = 20,
|
||||||
VHOST_USER_GET_CONFIG = 24,
|
VHOST_USER_GET_CONFIG = 24,
|
||||||
VHOST_USER_SET_CONFIG = 25,
|
VHOST_USER_SET_CONFIG = 25,
|
||||||
|
VHOST_USER_NVME_ADMIN = 27,
|
||||||
|
VHOST_USER_NVME_SET_CQ_CALL = 28,
|
||||||
|
VHOST_USER_NVME_GET_CAP = 29,
|
||||||
|
VHOST_USER_NVME_START_STOP = 30,
|
||||||
|
VHOST_USER_NVME_IO_CMD = 31,
|
||||||
VHOST_USER_MAX
|
VHOST_USER_MAX
|
||||||
} VhostUserRequest;
|
} VhostUserRequest;
|
||||||
|
|
||||||
@ -119,6 +124,17 @@ typedef struct VhostUserConfig {
|
|||||||
uint8_t region[VHOST_USER_MAX_CONFIG_SIZE];
|
uint8_t region[VHOST_USER_MAX_CONFIG_SIZE];
|
||||||
} VhostUserConfig;
|
} VhostUserConfig;
|
||||||
|
|
||||||
|
enum VhostUserNvmeQueueTypes {
|
||||||
|
VHOST_USER_NVME_SUBMISSION_QUEUE = 1,
|
||||||
|
VHOST_USER_NVME_COMPLETION_QUEUE = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct VhostUserNvmeIO {
|
||||||
|
enum VhostUserNvmeQueueTypes queue_type;
|
||||||
|
uint32_t qid;
|
||||||
|
uint32_t tail_head;
|
||||||
|
} VhostUserNvmeIO;
|
||||||
|
|
||||||
typedef struct VhostUserMsg {
|
typedef struct VhostUserMsg {
|
||||||
VhostUserRequest request;
|
VhostUserRequest request;
|
||||||
|
|
||||||
@ -136,6 +152,14 @@ typedef struct VhostUserMsg {
|
|||||||
VhostUserMemory memory;
|
VhostUserMemory memory;
|
||||||
VhostUserLog log;
|
VhostUserLog log;
|
||||||
VhostUserConfig config;
|
VhostUserConfig config;
|
||||||
|
struct nvme {
|
||||||
|
union {
|
||||||
|
uint8_t req[64];
|
||||||
|
uint8_t cqe[16];
|
||||||
|
} cmd;
|
||||||
|
uint8_t buf[4096];
|
||||||
|
} nvme;
|
||||||
|
struct VhostUserNvmeIO nvme_io;
|
||||||
} payload;
|
} payload;
|
||||||
int fds[VHOST_MEMORY_MAX_NREGIONS];
|
int fds[VHOST_MEMORY_MAX_NREGIONS];
|
||||||
} __attribute((packed)) VhostUserMsg;
|
} __attribute((packed)) VhostUserMsg;
|
||||||
|
@ -79,6 +79,9 @@ const struct vhost_device_ops g_spdk_vhost_ops = {
|
|||||||
.set_config = set_config,
|
.set_config = set_config,
|
||||||
.new_connection = new_connection,
|
.new_connection = new_connection,
|
||||||
.destroy_connection = destroy_connection,
|
.destroy_connection = destroy_connection,
|
||||||
|
.vhost_nvme_admin_passthrough = spdk_vhost_nvme_admin_passthrough,
|
||||||
|
.vhost_nvme_set_cq_call = spdk_vhost_nvme_set_cq_call,
|
||||||
|
.vhost_nvme_get_cap = spdk_vhost_nvme_get_cap,
|
||||||
};
|
};
|
||||||
|
|
||||||
static TAILQ_HEAD(, spdk_vhost_dev) g_spdk_vhost_devices = TAILQ_HEAD_INITIALIZER(
|
static TAILQ_HEAD(, spdk_vhost_dev) g_spdk_vhost_devices = TAILQ_HEAD_INITIALIZER(
|
||||||
@ -534,6 +537,7 @@ spdk_vhost_dev_mem_unregister(struct spdk_vhost_dev *vdev)
|
|||||||
assert(false);
|
assert(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -882,7 +886,6 @@ spdk_vhost_event_send(struct spdk_vhost_dev *vdev, spdk_vhost_event_fn cb_fn,
|
|||||||
|
|
||||||
ev_ctx.vdev = vdev;
|
ev_ctx.vdev = vdev;
|
||||||
ev_ctx.cb_fn = cb_fn;
|
ev_ctx.cb_fn = cb_fn;
|
||||||
|
|
||||||
ev = spdk_event_allocate(vdev->lcore, spdk_vhost_event_cb, &ev_ctx, NULL);
|
ev = spdk_event_allocate(vdev->lcore, spdk_vhost_event_cb, &ev_ctx, NULL);
|
||||||
assert(ev);
|
assert(ev);
|
||||||
spdk_event_call(ev);
|
spdk_event_call(ev);
|
||||||
@ -1290,6 +1293,12 @@ spdk_vhost_init(void)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ret = spdk_vhost_nvme_controller_construct();
|
||||||
|
if (ret != 0) {
|
||||||
|
SPDK_ERRLOG("Cannot construct vhost NVMe controllers\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -254,5 +254,14 @@ void spdk_vhost_dump_config_json(struct spdk_vhost_dev *vdev, struct spdk_json_w
|
|||||||
void spdk_vhost_dev_backend_event_done(void *event_ctx, int response);
|
void spdk_vhost_dev_backend_event_done(void *event_ctx, int response);
|
||||||
void spdk_vhost_lock(void);
|
void spdk_vhost_lock(void);
|
||||||
void spdk_vhost_unlock(void);
|
void spdk_vhost_unlock(void);
|
||||||
|
int spdk_remove_vhost_controller(struct spdk_vhost_dev *vdev);
|
||||||
|
int spdk_vhost_nvme_admin_passthrough(int vid, void *cmd, void *cqe, void *buf);
|
||||||
|
int spdk_vhost_nvme_set_cq_call(int vid, uint16_t qid, int fd);
|
||||||
|
int spdk_vhost_nvme_get_cap(int vid, uint64_t *cap);
|
||||||
|
int spdk_vhost_nvme_controller_construct(void);
|
||||||
|
int spdk_vhost_nvme_dev_construct(const char *name, const char *cpumask, uint32_t io_queues);
|
||||||
|
int spdk_vhost_nvme_dev_remove(struct spdk_vhost_dev *vdev);
|
||||||
|
int spdk_vhost_nvme_dev_add_ns(struct spdk_vhost_dev *vdev,
|
||||||
|
const char *bdev_name);
|
||||||
|
|
||||||
#endif /* SPDK_VHOST_INTERNAL_H */
|
#endif /* SPDK_VHOST_INTERNAL_H */
|
||||||
|
1296
lib/vhost/vhost_nvme.c
Normal file
1296
lib/vhost/vhost_nvme.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -108,6 +108,27 @@ DEFINE_STUB(spdk_env_get_current_core, uint32_t, (void), 0);
|
|||||||
|
|
||||||
static struct spdk_vhost_dev_backend g_vdev_backend;
|
static struct spdk_vhost_dev_backend g_vdev_backend;
|
||||||
|
|
||||||
|
int spdk_vhost_nvme_admin_passthrough(int vid, void *cmd, void *cqe, void *buf)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int spdk_vhost_nvme_set_cq_call(int vid, uint16_t qid, int fd)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int spdk_vhost_nvme_get_cap(int vid, uint64_t *cap)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
spdk_vhost_nvme_controller_construct(void)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
test_setup(void)
|
test_setup(void)
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user