@@ -24,6 +24,7 @@
#include "libqos/libqos.h"
#include "libqos/pci-pc.h"
#include "libqos/virtio-pci.h"
+#include "libqos/virtio-blk.h"
#include "libqos/malloc-pc.h"
#include "hw/virtio/virtio-net.h"
@@ -31,6 +32,7 @@
#include "standard-headers/linux/vhost_types.h"
#include "standard-headers/linux/virtio_ids.h"
#include "standard-headers/linux/virtio_net.h"
+#include "standard-headers/linux/virtio_blk.h"
#ifdef CONFIG_LINUX
#include <sys/vfs.h>
@@ -43,6 +45,7 @@
" -numa node,memdev=mem"
#define QEMU_CMD_CHR " -chardev socket,id=%s,path=%s%s"
#define QEMU_CMD_NETDEV " -netdev vhost-user,id=hs0,chardev=%s,vhostforce"
+#define QEMU_CMD_BLKCHR " -chardev socket,id=chdev0,path=%s%s"
#define HUGETLBFS_MAGIC 0x958458f6
@@ -55,6 +58,7 @@
#define VHOST_USER_PROTOCOL_F_MQ 0
#define VHOST_USER_PROTOCOL_F_LOG_SHMFD 1
#define VHOST_USER_PROTOCOL_F_CROSS_ENDIAN 6
+#define VHOST_USER_PROTOCOL_F_CONFIG 9
#define VHOST_LOG_PAGE 0x1000
@@ -78,6 +82,8 @@ typedef enum VhostUserRequest {
VHOST_USER_SET_PROTOCOL_FEATURES = 16,
VHOST_USER_GET_QUEUE_NUM = 17,
VHOST_USER_SET_VRING_ENABLE = 18,
+ VHOST_USER_GET_CONFIG = 24,
+ VHOST_USER_SET_CONFIG = 25,
VHOST_USER_MAX
} VhostUserRequest;
@@ -99,6 +105,14 @@ typedef struct VhostUserLog {
uint64_t mmap_offset;
} VhostUserLog;
+#define VHOST_USER_MAX_CONFIG_SIZE 256
+typedef struct VhostUserConfig {
+ uint32_t offset;
+ uint32_t size;
+ uint32_t flags;
+ uint8_t region[VHOST_USER_MAX_CONFIG_SIZE];
+} VhostUserConfig;
+
typedef struct VhostUserMsg {
VhostUserRequest request;
@@ -114,6 +128,7 @@ typedef struct VhostUserMsg {
struct vhost_vring_addr addr;
VhostUserMemory memory;
VhostUserLog log;
+ VhostUserConfig config;
} payload;
} QEMU_PACKED VhostUserMsg;
@@ -137,6 +152,7 @@ enum {
enum {
VHOST_USER_NET,
+ VHOST_USER_BLK,
};
typedef struct TestServer {
@@ -166,12 +182,15 @@ struct vhost_user_ops {
int type;
void (*append_opts)(TestServer *s, GString *cmd_line,
const char *chr_opts);
+ void (*driver_init)(void *obj, QGuestAllocator *alloc);
/* VHOST-USER commands. */
void (*set_features)(TestServer *s, CharBackend *chr,
VhostUserMsg *msg);
void (*get_protocol_features)(TestServer *s,
CharBackend *chr, VhostUserMsg *msg);
+ void (*get_config)(TestServer *s, CharBackend *chr,
+ VhostUserMsg *msg);
};
static const char *init_hugepagefs(void);
@@ -194,6 +213,14 @@ static void append_vhost_net_opts(TestServer *s, GString *cmd_line,
chr_opts, s->chr_name);
}
+static void append_vhost_blk_opts(TestServer *s, GString *cmd_line,
+ const char *chr_opts)
+{
+ g_string_append_printf(cmd_line, QEMU_CMD_BLKCHR,
+ s->socket_path,
+ chr_opts);
+}
+
static void append_mem_opts(TestServer *server, GString *cmd_line,
int size, enum test_memfd memfd)
{
@@ -425,6 +452,12 @@ static void chr_read(void *opaque, const uint8_t *buf, int size)
qemu_chr_fe_write_all(chr, p, VHOST_USER_HDR_SIZE + msg.size);
break;
+ case VHOST_USER_GET_CONFIG:
+ if (s->vu_ops->get_config) {
+ s->vu_ops->get_config(s, chr, &msg);
+ }
+ break;
+
default:
break;
}
@@ -727,6 +760,9 @@ static void test_migrate(void *obj, void *arg, QGuestAllocator *alloc)
guint8 *log;
guint64 size;
+ if (s->vu_ops->driver_init) {
+ s->vu_ops->driver_init(obj, alloc);
+ }
if (!wait_for_fds(s)) {
return;
}
@@ -796,6 +832,24 @@ static void test_migrate(void *obj, void *arg, QGuestAllocator *alloc)
g_string_free(dest_cmdline, true);
}
+static void vu_blk_driver_init(void *obj, QGuestAllocator *alloc)
+{
+ QVirtioBlk *blk_if;
+ QVirtioDevice *dev;
+ QVirtQueue *vq;
+ uint64_t features;
+
+ blk_if = obj;
+ dev = blk_if->vdev;
+ features = qvirtio_get_features(dev);
+ qvirtio_set_features(dev, features);
+
+ vq = qvirtqueue_setup(dev, alloc, 0);
+ g_assert(vq);
+
+ qvirtio_set_driver_ok(dev);
+}
+
static void wait_for_rings_started(TestServer *s, size_t count)
{
gint64 end_time;
@@ -857,12 +911,21 @@ static void test_reconnect(void *obj, void *arg, QGuestAllocator *alloc)
{
TestServer *s = arg;
GSource *src;
+ int nq;
+ if (s->vu_ops->driver_init) {
+ s->vu_ops->driver_init(obj, alloc);
+ }
if (!wait_for_fds(s)) {
return;
}
- wait_for_rings_started(s, 2);
+ nq = 1;
+ if (s->vu_ops->type == VHOST_USER_NET) {
+ /* tx and rx queues */
+ nq = 2;
+ }
+ wait_for_rings_started(s, nq);
/* reconnect */
s->fds_num = 0;
@@ -872,7 +935,7 @@ static void test_reconnect(void *obj, void *arg, QGuestAllocator *alloc)
g_source_attach(src, s->context);
g_source_unref(src);
g_assert(wait_for_fds(s));
- wait_for_rings_started(s, 2);
+ wait_for_rings_started(s, nq);
}
static void *vhost_user_test_setup_connect_fail(GString *cmd_line, void *arg)
@@ -960,6 +1023,56 @@ static void vu_net_get_protocol_features(TestServer *s, CharBackend *chr,
qemu_chr_fe_write_all(chr, (uint8_t *)msg, VHOST_USER_HDR_SIZE + msg->size);
}
+static void vu_blk_set_features(TestServer *s, CharBackend *chr,
+ VhostUserMsg *msg)
+{
+ if (s->test_flags == TEST_FLAGS_DISCONNECT) {
+ qemu_chr_fe_disconnect(chr);
+ s->test_flags = TEST_FLAGS_BAD;
+ }
+}
+
+static void vu_blk_get_protocol_features(TestServer *s,
+ CharBackend *chr, VhostUserMsg *msg)
+{
+ /* send back features to qemu */
+ msg->flags |= VHOST_USER_REPLY_MASK;
+ msg->size = sizeof(m.payload.u64);
+ msg->payload.u64 = 1 << VHOST_USER_PROTOCOL_F_LOG_SHMFD;
+ msg->payload.u64 |= 1 << VHOST_USER_PROTOCOL_F_CONFIG;
+ if (s->queues > 1) {
+ msg->payload.u64 |= 1 << VHOST_USER_PROTOCOL_F_MQ;
+ }
+ qemu_chr_fe_write_all(chr, (uint8_t *)msg, VHOST_USER_HDR_SIZE + msg->size);
+}
+
+static void vu_blk_get_config(TestServer *s, CharBackend *chr,
+ VhostUserMsg *msg)
+{
+ VhostUserConfig *config;
+ struct virtio_blk_config *blk_config;
+
+ config = &msg->payload.config;
+ memset(config, 0, sizeof(*config));
+ config->size = sizeof(*blk_config);
+
+ blk_config = (struct virtio_blk_config *)&config->region;
+ /*
+ * Represent 128Mb test disk, with no real backend, just
+ * to test vhost-user functionality.
+ */
+ blk_config->capacity = 262144;
+ blk_config->size_max = 0x20000;
+ blk_config->seg_max = 0x7e;
+ blk_config->blk_size = 512;
+ blk_config->min_io_size = 0x1;
+ blk_config->num_queues = 0x1;
+
+ msg->size = sizeof(*config) - sizeof(config->region) + config->size;
+ msg->flags |= VHOST_USER_REPLY_MASK;
+ qemu_chr_fe_write_all(chr, (uint8_t *)msg, VHOST_USER_HDR_SIZE + msg->size);
+}
+
/* Each VHOST-USER device should have its ops structure defined. */
static struct vhost_user_ops g_vu_net_ops = {
.type = VHOST_USER_NET,
@@ -970,6 +1083,17 @@ static struct vhost_user_ops g_vu_net_ops = {
.get_protocol_features = vu_net_get_protocol_features,
};
+static struct vhost_user_ops g_vu_blk_ops = {
+ .type = VHOST_USER_BLK,
+
+ .append_opts = append_vhost_blk_opts,
+ .driver_init = vu_blk_driver_init,
+
+ .set_features = vu_blk_set_features,
+ .get_protocol_features = vu_blk_get_protocol_features,
+ .get_config = vu_blk_get_config,
+};
+
static void register_vhost_user_test(void)
{
QOSGraphTestOptions opts = {
@@ -1015,5 +1139,16 @@ static void register_vhost_user_test(void)
qos_add_test("vhost-user/multiqueue",
"virtio-net",
test_multiqueue, &opts);
+ opts.edge.extra_device_opts = NULL;
+
+ /* vhost-user-blk tests */
+ opts.arg = &g_vu_blk_ops;
+ opts.before = vhost_user_test_setup_reconnect;
+ qos_add_test("reconnect", "vhost-user-blk",
+ test_reconnect, &opts);
+
+ opts.before = vhost_user_test_setup_memfd;
+ qos_add_test("migrate", "vhost-user-blk",
+ test_migrate, &opts);
}
libqos_init(register_vhost_user_test);