vhost-user-test: add live-migration test
This test checks that the log fd is given to the migration source, and mark dirty pages during migration. Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> Tested-by: Thibaut Collet <thibaut.collet@6wind.com>
This commit is contained in:
parent
704b216887
commit
b181974724
@ -12,6 +12,7 @@
|
||||
|
||||
#include "libqtest.h"
|
||||
#include "qemu/option.h"
|
||||
#include "qemu/range.h"
|
||||
#include "sysemu/char.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
|
||||
@ -47,6 +48,9 @@
|
||||
#define VHOST_MEMORY_MAX_NREGIONS 8
|
||||
|
||||
#define VHOST_USER_F_PROTOCOL_FEATURES 30
|
||||
#define VHOST_USER_PROTOCOL_F_LOG_SHMFD 1
|
||||
|
||||
#define VHOST_LOG_PAGE 0x1000
|
||||
|
||||
typedef enum VhostUserRequest {
|
||||
VHOST_USER_NONE = 0,
|
||||
@ -117,6 +121,7 @@ typedef struct TestServer {
|
||||
VhostUserMemory memory;
|
||||
GMutex data_mutex;
|
||||
GCond data_cond;
|
||||
int log_fd;
|
||||
} TestServer;
|
||||
|
||||
#if !GLIB_CHECK_VERSION(2, 32, 0)
|
||||
@ -238,7 +243,8 @@ static void chr_read(void *opaque, const uint8_t *buf, int size)
|
||||
/* send back features to qemu */
|
||||
msg.flags |= VHOST_USER_REPLY_MASK;
|
||||
msg.size = sizeof(m.u64);
|
||||
msg.u64 = 0x1ULL << VHOST_USER_F_PROTOCOL_FEATURES;
|
||||
msg.u64 = 0x1ULL << VHOST_F_LOG_ALL |
|
||||
0x1ULL << VHOST_USER_F_PROTOCOL_FEATURES;
|
||||
p = (uint8_t *) &msg;
|
||||
qemu_chr_fe_write_all(chr, p, VHOST_USER_HDR_SIZE + msg.size);
|
||||
break;
|
||||
@ -252,7 +258,7 @@ static void chr_read(void *opaque, const uint8_t *buf, int size)
|
||||
/* send back features to qemu */
|
||||
msg.flags |= VHOST_USER_REPLY_MASK;
|
||||
msg.size = sizeof(m.u64);
|
||||
msg.u64 = 0;
|
||||
msg.u64 = 1 << VHOST_USER_PROTOCOL_F_LOG_SHMFD;
|
||||
p = (uint8_t *) &msg;
|
||||
qemu_chr_fe_write_all(chr, p, VHOST_USER_HDR_SIZE + msg.size);
|
||||
break;
|
||||
@ -286,6 +292,21 @@ static void chr_read(void *opaque, const uint8_t *buf, int size)
|
||||
*/
|
||||
qemu_set_nonblock(fd);
|
||||
break;
|
||||
|
||||
case VHOST_USER_SET_LOG_BASE:
|
||||
if (s->log_fd != -1) {
|
||||
close(s->log_fd);
|
||||
s->log_fd = -1;
|
||||
}
|
||||
qemu_chr_fe_get_msgfds(chr, &s->log_fd, 1);
|
||||
msg.flags |= VHOST_USER_REPLY_MASK;
|
||||
msg.size = 0;
|
||||
p = (uint8_t *) &msg;
|
||||
qemu_chr_fe_write_all(chr, p, VHOST_USER_HDR_SIZE);
|
||||
|
||||
g_cond_signal(&s->data_cond);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -337,6 +358,8 @@ static TestServer *test_server_new(const gchar *name)
|
||||
g_mutex_init(&server->data_mutex);
|
||||
g_cond_init(&server->data_cond);
|
||||
|
||||
server->log_fd = -1;
|
||||
|
||||
return server;
|
||||
}
|
||||
|
||||
@ -358,12 +381,155 @@ static void test_server_free(TestServer *server)
|
||||
close(server->fds[i]);
|
||||
}
|
||||
|
||||
if (server->log_fd != -1) {
|
||||
close(server->log_fd);
|
||||
}
|
||||
|
||||
unlink(server->socket_path);
|
||||
g_free(server->socket_path);
|
||||
|
||||
|
||||
g_free(server->chr_name);
|
||||
g_free(server);
|
||||
}
|
||||
|
||||
static void wait_for_log_fd(TestServer *s)
|
||||
{
|
||||
gint64 end_time;
|
||||
|
||||
g_mutex_lock(&s->data_mutex);
|
||||
end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND;
|
||||
while (s->log_fd == -1) {
|
||||
if (!g_cond_wait_until(&s->data_cond, &s->data_mutex, end_time)) {
|
||||
/* timeout has passed */
|
||||
g_assert(s->log_fd != -1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
g_mutex_unlock(&s->data_mutex);
|
||||
}
|
||||
|
||||
static void write_guest_mem(TestServer *s, uint32 seed)
|
||||
{
|
||||
uint32_t *guest_mem;
|
||||
int i, j;
|
||||
size_t size;
|
||||
|
||||
wait_for_fds(s);
|
||||
|
||||
/* iterate all regions */
|
||||
for (i = 0; i < s->fds_num; i++) {
|
||||
|
||||
/* We'll write only the region statring at 0x0 */
|
||||
if (s->memory.regions[i].guest_phys_addr != 0x0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
g_assert_cmpint(s->memory.regions[i].memory_size, >, 1024);
|
||||
|
||||
size = s->memory.regions[i].memory_size +
|
||||
s->memory.regions[i].mmap_offset;
|
||||
|
||||
guest_mem = mmap(0, size, PROT_READ | PROT_WRITE,
|
||||
MAP_SHARED, s->fds[i], 0);
|
||||
|
||||
g_assert(guest_mem != MAP_FAILED);
|
||||
guest_mem += (s->memory.regions[i].mmap_offset / sizeof(*guest_mem));
|
||||
|
||||
for (j = 0; j < 256; j++) {
|
||||
guest_mem[j] = seed + j;
|
||||
}
|
||||
|
||||
munmap(guest_mem, s->memory.regions[i].memory_size);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static guint64 get_log_size(TestServer *s)
|
||||
{
|
||||
guint64 log_size = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < s->memory.nregions; ++i) {
|
||||
VhostUserMemoryRegion *reg = &s->memory.regions[i];
|
||||
guint64 last = range_get_last(reg->guest_phys_addr,
|
||||
reg->memory_size);
|
||||
log_size = MAX(log_size, last / (8 * VHOST_LOG_PAGE) + 1);
|
||||
}
|
||||
|
||||
return log_size;
|
||||
}
|
||||
|
||||
static void test_migrate(void)
|
||||
{
|
||||
TestServer *s = test_server_new("src");
|
||||
TestServer *dest = test_server_new("dest");
|
||||
const char *uri = "tcp:127.0.0.1:1234";
|
||||
QTestState *global = global_qtest, *from, *to;
|
||||
gchar *cmd;
|
||||
QDict *rsp;
|
||||
guint8 *log;
|
||||
guint64 size;
|
||||
|
||||
cmd = GET_QEMU_CMDE(s, 2, "");
|
||||
from = qtest_start(cmd);
|
||||
g_free(cmd);
|
||||
|
||||
wait_for_fds(s);
|
||||
size = get_log_size(s);
|
||||
g_assert_cmpint(size, ==, (2 * 1024 * 1024) / (VHOST_LOG_PAGE * 8));
|
||||
|
||||
cmd = GET_QEMU_CMDE(dest, 2, " -incoming %s", uri);
|
||||
to = qtest_init(cmd);
|
||||
g_free(cmd);
|
||||
|
||||
/* slow down migration to have time to fiddle with log */
|
||||
/* TODO: qtest could learn to break on some places */
|
||||
rsp = qmp("{ 'execute': 'migrate_set_speed',"
|
||||
"'arguments': { 'value': 10 } }");
|
||||
g_assert(qdict_haskey(rsp, "return"));
|
||||
QDECREF(rsp);
|
||||
|
||||
cmd = g_strdup_printf("{ 'execute': 'migrate',"
|
||||
"'arguments': { 'uri': '%s' } }",
|
||||
uri);
|
||||
rsp = qmp(cmd);
|
||||
g_free(cmd);
|
||||
g_assert(qdict_haskey(rsp, "return"));
|
||||
QDECREF(rsp);
|
||||
|
||||
wait_for_log_fd(s);
|
||||
|
||||
log = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, s->log_fd, 0);
|
||||
g_assert(log != MAP_FAILED);
|
||||
|
||||
/* modify first page */
|
||||
write_guest_mem(s, 0x42);
|
||||
log[0] = 1;
|
||||
munmap(log, size);
|
||||
|
||||
/* speed things up */
|
||||
rsp = qmp("{ 'execute': 'migrate_set_speed',"
|
||||
"'arguments': { 'value': 0 } }");
|
||||
g_assert(qdict_haskey(rsp, "return"));
|
||||
QDECREF(rsp);
|
||||
|
||||
qmp_eventwait("STOP");
|
||||
|
||||
global_qtest = to;
|
||||
qmp_eventwait("RESUME");
|
||||
|
||||
read_guest_mem(dest);
|
||||
|
||||
qtest_quit(to);
|
||||
test_server_free(dest);
|
||||
qtest_quit(from);
|
||||
test_server_free(s);
|
||||
|
||||
global_qtest = global;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
QTestState *s = NULL;
|
||||
@ -403,6 +569,7 @@ int main(int argc, char **argv)
|
||||
g_free(qemu_cmd);
|
||||
|
||||
qtest_add_data_func("/vhost-user/read-guest-mem", server, read_guest_mem);
|
||||
qtest_add_func("/vhost-user/migrate", test_migrate);
|
||||
|
||||
ret = g_test_run();
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user