summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--docs/devel/migration.rst12
-rw-r--r--hmp-commands.hx24
-rw-r--r--hw/core/clock-vmstate.c1
-rw-r--r--hw/ssi/xlnx-versal-ospi.c1
-rw-r--r--include/migration/vmstate.h2
-rw-r--r--meson.build13
-rw-r--r--migration/migration.c26
-rw-r--r--migration/migration.h48
-rw-r--r--migration/postcopy-ram.c108
-rw-r--r--migration/postcopy-ram.h4
-rw-r--r--migration/ram.c64
-rw-r--r--migration/rdma.c7
-rw-r--r--migration/savevm.c46
-rw-r--r--migration/trace-events7
-rw-r--r--migration/vmstate.c6
-rw-r--r--monitor/hmp-cmds.c47
-rw-r--r--monitor/hmp.c19
-rw-r--r--monitor/monitor-internal.h3
-rw-r--r--monitor/qmp-cmds.c49
-rw-r--r--qapi/ui.json120
-rw-r--r--tests/qtest/migration-test.c27
-rw-r--r--tools/virtiofsd/passthrough_ll.c2
22 files changed, 435 insertions, 201 deletions
diff --git a/docs/devel/migration.rst b/docs/devel/migration.rst
index 2401253482..3e9656d8e0 100644
--- a/docs/devel/migration.rst
+++ b/docs/devel/migration.rst
@@ -389,19 +389,13 @@ Each version is associated with a series of fields saved. The ``save_state`` al
the state as the newer version. But ``load_state`` sometimes is able to
load state from an older version.
-You can see that there are several version fields:
+You can see that there are two version fields:
- ``version_id``: the maximum version_id supported by VMState for that device.
- ``minimum_version_id``: the minimum version_id that VMState is able to understand
for that device.
-- ``minimum_version_id_old``: For devices that were not able to port to vmstate, we can
- assign a function that knows how to read this old state. This field is
- ignored if there is no ``load_state_old`` handler.
-
-VMState is able to read versions from minimum_version_id to
-version_id. And the function ``load_state_old()`` (if present) is able to
-load state from minimum_version_id_old to minimum_version_id. This
-function is deprecated and will be removed when no more users are left.
+
+VMState is able to read versions from minimum_version_id to version_id.
There are *_V* forms of many ``VMSTATE_`` macros to load fields for version dependent fields,
e.g.
diff --git a/hmp-commands.hx b/hmp-commands.hx
index 70a9136ac2..8476277aa9 100644
--- a/hmp-commands.hx
+++ b/hmp-commands.hx
@@ -1514,33 +1514,35 @@ ERST
{
.name = "set_password",
- .args_type = "protocol:s,password:s,connected:s?",
- .params = "protocol password action-if-connected",
+ .args_type = "protocol:s,password:s,display:-ds,connected:s?",
+ .params = "protocol password [-d display] [action-if-connected]",
.help = "set spice/vnc password",
.cmd = hmp_set_password,
},
SRST
-``set_password [ vnc | spice ] password [ action-if-connected ]``
- Change spice/vnc password. *action-if-connected* specifies what
- should happen in case a connection is established: *fail* makes the
- password change fail. *disconnect* changes the password and
+``set_password [ vnc | spice ] password [ -d display ] [ action-if-connected ]``
+ Change spice/vnc password. *display* can be used with 'vnc' to specify
+ which display to set the password on. *action-if-connected* specifies
+ what should happen in case a connection is established: *fail* makes
+ the password change fail. *disconnect* changes the password and
disconnects the client. *keep* changes the password and keeps the
connection up. *keep* is the default.
ERST
{
.name = "expire_password",
- .args_type = "protocol:s,time:s",
- .params = "protocol time",
+ .args_type = "protocol:s,time:s,display:-ds",
+ .params = "protocol time [-d display]",
.help = "set spice/vnc password expire-time",
.cmd = hmp_expire_password,
},
SRST
-``expire_password [ vnc | spice ]`` *expire-time*
- Specify when a password for spice/vnc becomes
- invalid. *expire-time* accepts:
+``expire_password [ vnc | spice ] expire-time [ -d display ]``
+ Specify when a password for spice/vnc becomes invalid.
+ *display* behaves the same as in ``set_password``.
+ *expire-time* accepts:
``now``
Invalidate password instantly.
diff --git a/hw/core/clock-vmstate.c b/hw/core/clock-vmstate.c
index 9d9174ffbd..7eccb6d4ea 100644
--- a/hw/core/clock-vmstate.c
+++ b/hw/core/clock-vmstate.c
@@ -44,6 +44,7 @@ const VMStateDescription vmstate_muldiv = {
.fields = (VMStateField[]) {
VMSTATE_UINT32(multiplier, Clock),
VMSTATE_UINT32(divider, Clock),
+ VMSTATE_END_OF_LIST()
},
};
diff --git a/hw/ssi/xlnx-versal-ospi.c b/hw/ssi/xlnx-versal-ospi.c
index 7ecd148fdf..c762e0b367 100644
--- a/hw/ssi/xlnx-versal-ospi.c
+++ b/hw/ssi/xlnx-versal-ospi.c
@@ -1800,7 +1800,6 @@ static const VMStateDescription vmstate_xlnx_versal_ospi = {
.name = TYPE_XILINX_VERSAL_OSPI,
.version_id = 1,
.minimum_version_id = 1,
- .minimum_version_id_old = 1,
.fields = (VMStateField[]) {
VMSTATE_FIFO8(rx_fifo, XlnxVersalOspi),
VMSTATE_FIFO8(tx_fifo, XlnxVersalOspi),
diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h
index 017c03675c..ad24aa1934 100644
--- a/include/migration/vmstate.h
+++ b/include/migration/vmstate.h
@@ -181,9 +181,7 @@ struct VMStateDescription {
int unmigratable;
int version_id;
int minimum_version_id;
- int minimum_version_id_old;
MigrationPriority priority;
- LoadStateHandler *load_state_old;
int (*pre_load)(void *opaque);
int (*post_load)(void *opaque, int version_id);
int (*pre_save)(void *opaque);
diff --git a/meson.build b/meson.build
index 8df40bfac4..a5b63e62cd 100644
--- a/meson.build
+++ b/meson.build
@@ -1306,6 +1306,18 @@ statx_test = gnu_source_prefix + '''
has_statx = cc.links(statx_test)
+# Check whether statx() provides mount ID information
+
+statx_mnt_id_test = gnu_source_prefix + '''
+ #include <sys/stat.h>
+ int main(void) {
+ struct statx statxbuf;
+ statx(0, "", 0, STATX_BASIC_STATS | STATX_MNT_ID, &statxbuf);
+ return statxbuf.stx_mnt_id;
+ }'''
+
+has_statx_mnt_id = cc.links(statx_mnt_id_test)
+
have_vhost_user_blk_server = get_option('vhost_user_blk_server') \
.require(targetos == 'linux',
error_message: 'vhost_user_blk_server requires linux') \
@@ -1553,6 +1565,7 @@ config_host_data.set('CONFIG_NETTLE', nettle.found())
config_host_data.set('CONFIG_QEMU_PRIVATE_XTS', xts == 'private')
config_host_data.set('CONFIG_MALLOC_TRIM', has_malloc_trim)
config_host_data.set('CONFIG_STATX', has_statx)
+config_host_data.set('CONFIG_STATX_MNT_ID', has_statx_mnt_id)
config_host_data.set('CONFIG_ZSTD', zstd.found())
config_host_data.set('CONFIG_FUSE', fuse.found())
config_host_data.set('CONFIG_FUSE_LSEEK', fuse_lseek.found())
diff --git a/migration/migration.c b/migration/migration.c
index bcc385b94b..9cc344514b 100644
--- a/migration/migration.c
+++ b/migration/migration.c
@@ -267,6 +267,19 @@ MigrationIncomingState *migration_incoming_get_current(void)
return current_incoming;
}
+void migration_incoming_transport_cleanup(MigrationIncomingState *mis)
+{
+ if (mis->socket_address_list) {
+ qapi_free_SocketAddressList(mis->socket_address_list);
+ mis->socket_address_list = NULL;
+ }
+
+ if (mis->transport_cleanup) {
+ mis->transport_cleanup(mis->transport_data);
+ mis->transport_data = mis->transport_cleanup = NULL;
+ }
+}
+
void migration_incoming_state_destroy(void)
{
struct MigrationIncomingState *mis = migration_incoming_get_current();
@@ -287,10 +300,8 @@ void migration_incoming_state_destroy(void)
g_array_free(mis->postcopy_remote_fds, TRUE);
mis->postcopy_remote_fds = NULL;
}
- if (mis->transport_cleanup) {
- mis->transport_cleanup(mis->transport_data);
- }
+ migration_incoming_transport_cleanup(mis);
qemu_event_reset(&mis->main_thread_load_event);
if (mis->page_requested) {
@@ -298,11 +309,6 @@ void migration_incoming_state_destroy(void)
mis->page_requested = NULL;
}
- if (mis->socket_address_list) {
- qapi_free_SocketAddressList(mis->socket_address_list);
- mis->socket_address_list = NULL;
- }
-
yank_unregister_instance(MIGRATION_YANK_INSTANCE);
}
@@ -2865,7 +2871,7 @@ retry:
out:
res = qemu_file_get_error(rp);
if (res) {
- if (res == -EIO && migration_in_postcopy()) {
+ if (res && migration_in_postcopy()) {
/*
* Maybe there is something we can do: it looks like a
* network down issue, and we pause for a recovery.
@@ -3466,7 +3472,7 @@ static MigThrError migration_detect_error(MigrationState *s)
error_free(local_error);
}
- if (state == MIGRATION_STATUS_POSTCOPY_ACTIVE && ret == -EIO) {
+ if (state == MIGRATION_STATUS_POSTCOPY_ACTIVE && ret) {
/*
* For postcopy, we allow the network to be down for a
* while. After that, it can be continued by a
diff --git a/migration/migration.h b/migration/migration.h
index 8130b703eb..2de861df01 100644
--- a/migration/migration.h
+++ b/migration/migration.h
@@ -45,14 +45,37 @@ struct PostcopyBlocktimeContext;
*/
#define CLEAR_BITMAP_SHIFT_MAX 31
+/* This is an abstraction of a "temp huge page" for postcopy's purpose */
+typedef struct {
+ /*
+ * This points to a temporary huge page as a buffer for UFFDIO_COPY. It's
+ * mmap()ed and needs to be freed when cleanup.
+ */
+ void *tmp_huge_page;
+ /*
+ * This points to the host page we're going to install for this temp page.
+ * It tells us after we've received the whole page, where we should put it.
+ */
+ void *host_addr;
+ /* Number of small pages copied (in size of TARGET_PAGE_SIZE) */
+ unsigned int target_pages;
+ /* Whether this page contains all zeros */
+ bool all_zero;
+} PostcopyTmpPage;
+
/* State for the incoming migration */
struct MigrationIncomingState {
QEMUFile *from_src_file;
-
+ /* Previously received RAM's RAMBlock pointer */
+ RAMBlock *last_recv_block;
/* A hook to allow cleanup at the end of incoming migration */
void *transport_data;
void (*transport_cleanup)(void *data);
-
+ /*
+ * Used to sync thread creations. Note that we can't create threads in
+ * parallel with this sem.
+ */
+ QemuSemaphore thread_sync_sem;
/*
* Free at the start of the main state load, set as the main thread finishes
* loading state.
@@ -65,13 +88,11 @@ struct MigrationIncomingState {
size_t largest_page_size;
bool have_fault_thread;
QemuThread fault_thread;
- QemuSemaphore fault_thread_sem;
/* Set this when we want the fault thread to quit */
bool fault_thread_quit;
bool have_listen_thread;
QemuThread listen_thread;
- QemuSemaphore listen_thread_sem;
/* For the kernel to send us notifications */
int userfault_fd;
@@ -81,7 +102,22 @@ struct MigrationIncomingState {
QemuMutex rp_mutex; /* We send replies from multiple threads */
/* RAMBlock of last request sent to source */
RAMBlock *last_rb;
- void *postcopy_tmp_page;
+ /*
+ * Number of postcopy channels including the default precopy channel, so
+ * vanilla postcopy will only contain one channel which contain both
+ * precopy and postcopy streams.
+ *
+ * This is calculated when the src requests to enable postcopy but before
+ * it starts. Its value can depend on e.g. whether postcopy preemption is
+ * enabled.
+ */
+ unsigned int postcopy_channels;
+ /*
+ * An array of temp host huge pages to be used, one for each postcopy
+ * channel.
+ */
+ PostcopyTmpPage *postcopy_tmp_pages;
+ /* This is shared for all postcopy channels */
void *postcopy_tmp_zero_page;
/* PostCopyFD's for external userfaultfds & handlers of shared memory */
GArray *postcopy_remote_fds;
@@ -130,6 +166,7 @@ struct MigrationIncomingState {
MigrationIncomingState *migration_incoming_get_current(void);
void migration_incoming_state_destroy(void);
+void migration_incoming_transport_cleanup(MigrationIncomingState *mis);
/*
* Functions to work with blocktime context
*/
@@ -391,5 +428,6 @@ bool migration_rate_limit(void);
void migration_cancel(const Error *error);
void populate_vfio_info(MigrationInfo *info);
+void postcopy_temp_page_reset(PostcopyTmpPage *tmp_page);
#endif
diff --git a/migration/postcopy-ram.c b/migration/postcopy-ram.c
index 2a2cc5faf8..32c52f4b1d 100644
--- a/migration/postcopy-ram.c
+++ b/migration/postcopy-ram.c
@@ -78,6 +78,20 @@ int postcopy_notify(enum PostcopyNotifyReason reason, Error **errp)
&pnd);
}
+/*
+ * NOTE: this routine is not thread safe, we can't call it concurrently. But it
+ * should be good enough for migration's purposes.
+ */
+void postcopy_thread_create(MigrationIncomingState *mis,
+ QemuThread *thread, const char *name,
+ void *(*fn)(void *), int joinable)
+{
+ qemu_sem_init(&mis->thread_sync_sem, 0);
+ qemu_thread_create(thread, name, fn, mis, joinable);
+ qemu_sem_wait(&mis->thread_sync_sem);
+ qemu_sem_destroy(&mis->thread_sync_sem);
+}
+
/* Postcopy needs to detect accesses to pages that haven't yet been copied
* across, and efficiently map new pages in, the techniques for doing this
* are target OS specific.
@@ -526,9 +540,18 @@ int postcopy_ram_incoming_init(MigrationIncomingState *mis)
static void postcopy_temp_pages_cleanup(MigrationIncomingState *mis)
{
- if (mis->postcopy_tmp_page) {
- munmap(mis->postcopy_tmp_page, mis->largest_page_size);
- mis->postcopy_tmp_page = NULL;
+ int i;
+
+ if (mis->postcopy_tmp_pages) {
+ for (i = 0; i < mis->postcopy_channels; i++) {
+ if (mis->postcopy_tmp_pages[i].tmp_huge_page) {
+ munmap(mis->postcopy_tmp_pages[i].tmp_huge_page,
+ mis->largest_page_size);
+ mis->postcopy_tmp_pages[i].tmp_huge_page = NULL;
+ }
+ }
+ g_free(mis->postcopy_tmp_pages);
+ mis->postcopy_tmp_pages = NULL;
}
if (mis->postcopy_tmp_zero_page) {
@@ -868,15 +891,11 @@ static void mark_postcopy_blocktime_end(uintptr_t addr)
affected_cpu);
}
-static bool postcopy_pause_fault_thread(MigrationIncomingState *mis)
+static void postcopy_pause_fault_thread(MigrationIncomingState *mis)
{
trace_postcopy_pause_fault_thread();
-
qemu_sem_wait(&mis->postcopy_pause_sem_fault);
-
trace_postcopy_pause_fault_thread_continued();
-
- return true;
}
/*
@@ -893,7 +912,7 @@ static void *postcopy_ram_fault_thread(void *opaque)
trace_postcopy_ram_fault_thread_entry();
rcu_register_thread();
mis->last_rb = NULL; /* last RAMBlock we sent part of */
- qemu_sem_post(&mis->fault_thread_sem);
+ qemu_sem_post(&mis->thread_sync_sem);
struct pollfd *pfd;
size_t pfd_len = 2 + mis->postcopy_remote_fds->len;
@@ -936,13 +955,7 @@ static void *postcopy_ram_fault_thread(void *opaque)
* broken already using the event. We should hold until
* the channel is rebuilt.
*/
- if (postcopy_pause_fault_thread(mis)) {
- /* Continue to read the userfaultfd */
- } else {
- error_report("%s: paused but don't allow to continue",
- __func__);
- break;
- }
+ postcopy_pause_fault_thread(mis);
}
if (pfd[1].revents) {
@@ -1016,15 +1029,8 @@ retry:
msg.arg.pagefault.address);
if (ret) {
/* May be network failure, try to wait for recovery */
- if (ret == -EIO && postcopy_pause_fault_thread(mis)) {
- /* We got reconnected somehow, try to continue */
- goto retry;
- } else {
- /* This is a unavoidable fault */
- error_report("%s: postcopy_request_page() get %d",
- __func__, ret);
- break;
- }
+ postcopy_pause_fault_thread(mis);
+ goto retry;
}
}
@@ -1092,17 +1098,30 @@ retry:
static int postcopy_temp_pages_setup(MigrationIncomingState *mis)
{
- int err;
-
- mis->postcopy_tmp_page = mmap(NULL, mis->largest_page_size,
- PROT_READ | PROT_WRITE,
- MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
- if (mis->postcopy_tmp_page == MAP_FAILED) {
- err = errno;
- mis->postcopy_tmp_page = NULL;
- error_report("%s: Failed to map postcopy_tmp_page %s",
- __func__, strerror(err));
- return -err;
+ PostcopyTmpPage *tmp_page;
+ int err, i, channels;
+ void *temp_page;
+
+ /* TODO: will be boosted when enable postcopy preemption */
+ mis->postcopy_channels = 1;
+
+ channels = mis->postcopy_channels;
+ mis->postcopy_tmp_pages = g_malloc0_n(sizeof(PostcopyTmpPage), channels);
+
+ for (i = 0; i < channels; i++) {
+ tmp_page = &mis->postcopy_tmp_pages[i];
+ temp_page = mmap(NULL, mis->largest_page_size, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ if (temp_page == MAP_FAILED) {
+ err = errno;
+ error_report("%s: Failed to map postcopy_tmp_pages[%d]: %s",
+ __func__, i, strerror(err));
+ /* Clean up will be done later */
+ return -err;
+ }
+ tmp_page->tmp_huge_page = temp_page;
+ /* Initialize default states for each tmp page */
+ postcopy_temp_page_reset(tmp_page);
}
/*
@@ -1151,11 +1170,8 @@ int postcopy_ram_incoming_setup(MigrationIncomingState *mis)
return -1;
}
- qemu_sem_init(&mis->fault_thread_sem, 0);
- qemu_thread_create(&mis->fault_thread, "postcopy/fault",
- postcopy_ram_fault_thread, mis, QEMU_THREAD_JOINABLE);
- qemu_sem_wait(&mis->fault_thread_sem);
- qemu_sem_destroy(&mis->fault_thread_sem);
+ postcopy_thread_create(mis, &mis->fault_thread, "postcopy/fault",
+ postcopy_ram_fault_thread, QEMU_THREAD_JOINABLE);
mis->have_fault_thread = true;
/* Mark so that we get notified of accesses to unwritten areas */
@@ -1352,6 +1368,16 @@ int postcopy_wake_shared(struct PostCopyFD *pcfd,
#endif
/* ------------------------------------------------------------------------- */
+void postcopy_temp_page_reset(PostcopyTmpPage *tmp_page)
+{
+ tmp_page->target_pages = 0;
+ tmp_page->host_addr = NULL;
+ /*
+ * This is set to true when reset, and cleared as long as we received any
+ * of the non-zero small page within this huge page.
+ */
+ tmp_page->all_zero = true;
+}
void postcopy_fault_thread_notify(MigrationIncomingState *mis)
{
diff --git a/migration/postcopy-ram.h b/migration/postcopy-ram.h
index 6d2b3cf124..07684c0e1d 100644
--- a/migration/postcopy-ram.h
+++ b/migration/postcopy-ram.h
@@ -135,6 +135,10 @@ void postcopy_remove_notifier(NotifierWithReturn *n);
/* Call the notifier list set by postcopy_add_start_notifier */
int postcopy_notify(enum PostcopyNotifyReason reason, Error **errp);
+void postcopy_thread_create(MigrationIncomingState *mis,
+ QemuThread *thread, const char *name,
+ void *(*fn)(void *), int joinable);
+
struct PostCopyFD;
/* ufd is a pointer to the struct uffd_msg *TODO: more Portable! */
diff --git a/migration/ram.c b/migration/ram.c
index 781f0745dc..170e522a1f 100644
--- a/migration/ram.c
+++ b/migration/ram.c
@@ -3185,12 +3185,14 @@ static int load_xbzrle(QEMUFile *f, ram_addr_t addr, void *host)
*
* Returns a pointer from within the RCU-protected ram_list.
*
+ * @mis: the migration incoming state pointer
* @f: QEMUFile where to read the data from
* @flags: Page flags (mostly to see if it's a continuation of previous block)
*/
-static inline RAMBlock *ram_block_from_stream(QEMUFile *f, int flags)
+static inline RAMBlock *ram_block_from_stream(MigrationIncomingState *mis,
+ QEMUFile *f, int flags)
{
- static RAMBlock *block;
+ RAMBlock *block = mis->last_recv_block;
char id[256];
uint8_t len;
@@ -3217,6 +3219,8 @@ static inline RAMBlock *ram_block_from_stream(QEMUFile *f, int flags)
return NULL;
}
+ mis->last_recv_block = block;
+
return block;
}
@@ -3641,11 +3645,8 @@ static int ram_load_postcopy(QEMUFile *f)
bool place_needed = false;
bool matches_target_page_size = false;
MigrationIncomingState *mis = migration_incoming_get_current();
- /* Temporary page that is later 'placed' */
- void *postcopy_host_page = mis->postcopy_tmp_page;
- void *host_page = NULL;
- bool all_zero = true;
- int target_pages = 0;
+ /* Currently we only use channel 0. TODO: use all the channels */
+ PostcopyTmpPage *tmp_page = &mis->postcopy_tmp_pages[0];
while (!ret && !(flags & RAM_SAVE_FLAG_EOS)) {
ram_addr_t addr;
@@ -3672,7 +3673,7 @@ static int ram_load_postcopy(QEMUFile *f)
trace_ram_load_postcopy_loop((uint64_t)addr, flags);
if (flags & (RAM_SAVE_FLAG_ZERO | RAM_SAVE_FLAG_PAGE |
RAM_SAVE_FLAG_COMPRESS_PAGE)) {
- block = ram_block_from_stream(f, flags);
+ block = ram_block_from_stream(mis, f, flags);
if (!block) {
ret = -EINVAL;
break;
@@ -3689,7 +3690,7 @@ static int ram_load_postcopy(QEMUFile *f)
ret = -EINVAL;
break;
}
- target_pages++;
+ tmp_page->target_pages++;
matches_target_page_size = block->page_size == TARGET_PAGE_SIZE;
/*
* Postcopy requires that we place whole host pages atomically;
@@ -3701,16 +3702,21 @@ static int ram_load_postcopy(QEMUFile *f)
* however the source ensures it always sends all the components
* of a host page in one chunk.
*/
- page_buffer = postcopy_host_page +
+ page_buffer = tmp_page->tmp_huge_page +
host_page_offset_from_ram_block_offset(block, addr);
/* If all TP are zero then we can optimise the place */
- if (target_pages == 1) {
- host_page = host_page_from_ram_block_offset(block, addr);
- } else if (host_page != host_page_from_ram_block_offset(block,
- addr)) {
+ if (tmp_page->target_pages == 1) {
+ tmp_page->host_addr =
+ host_page_from_ram_block_offset(block, addr);
+ } else if (tmp_page->host_addr !=
+ host_page_from_ram_block_offset(block, addr)) {
/* not the 1st TP within the HP */
- error_report("Non-same host page %p/%p", host_page,
- host_page_from_ram_block_offset(block, addr));
+ error_report("Non-same host page detected. "
+ "Target host page %p, received host page %p "
+ "(rb %s offset 0x"RAM_ADDR_FMT" target_pages %d)",
+ tmp_page->host_addr,
+ host_page_from_ram_block_offset(block, addr),
+ block->idstr, addr, tmp_page->target_pages);
ret = -EINVAL;
break;
}
@@ -3719,10 +3725,11 @@ static int ram_load_postcopy(QEMUFile *f)
* If it's the last part of a host page then we place the host
* page
*/
- if (target_pages == (block->page_size / TARGET_PAGE_SIZE)) {
+ if (tmp_page->target_pages ==
+ (block->page_size / TARGET_PAGE_SIZE)) {
place_needed = true;
}
- place_source = postcopy_host_page;
+ place_source = tmp_page->tmp_huge_page;
}
switch (flags & ~RAM_SAVE_FLAG_CONTINUE) {
@@ -3736,12 +3743,12 @@ static int ram_load_postcopy(QEMUFile *f)
memset(page_buffer, ch, TARGET_PAGE_SIZE);
}
if (ch) {
- all_zero = false;
+ tmp_page->all_zero = false;
}
break;
case RAM_SAVE_FLAG_PAGE:
- all_zero = false;
+ tmp_page->all_zero = false;
if (!matches_target_page_size) {
/* For huge pages, we always use temporary buffer */
qemu_get_buffer(f, page_buffer, TARGET_PAGE_SIZE);
@@ -3759,7 +3766,7 @@ static int ram_load_postcopy(QEMUFile *f)
}
break;
case RAM_SAVE_FLAG_COMPRESS_PAGE:
- all_zero = false;
+ tmp_page->all_zero = false;
len = qemu_get_be32(f);
if (len < 0 || len > compressBound(TARGET_PAGE_SIZE)) {
error_report("Invalid compressed data length: %d", len);
@@ -3791,16 +3798,14 @@ static int ram_load_postcopy(QEMUFile *f)
}
if (!ret && place_needed) {
- if (all_zero) {
- ret = postcopy_place_page_zero(mis, host_page, block);
+ if (tmp_page->all_zero) {
+ ret = postcopy_place_page_zero(mis, tmp_page->host_addr, block);
} else {
- ret = postcopy_place_page(mis, host_page, place_source,
- block);
+ ret = postcopy_place_page(mis, tmp_page->host_addr,
+ place_source, block);
}
place_needed = false;
- target_pages = 0;
- /* Assume we have a zero page until we detect something different */
- all_zero = true;
+ postcopy_temp_page_reset(tmp_page);
}
}
@@ -3880,6 +3885,7 @@ void colo_flush_ram_cache(void)
*/
static int ram_load_precopy(QEMUFile *f)
{
+ MigrationIncomingState *mis = migration_incoming_get_current();
int flags = 0, ret = 0, invalid_flags = 0, len = 0, i = 0;
/* ADVISE is earlier, it shows the source has the postcopy capability on */
bool postcopy_advised = postcopy_is_advised();
@@ -3918,7 +3924,7 @@ static int ram_load_precopy(QEMUFile *f)
if (flags & (RAM_SAVE_FLAG_ZERO | RAM_SAVE_FLAG_PAGE |
RAM_SAVE_FLAG_COMPRESS_PAGE | RAM_SAVE_FLAG_XBZRLE)) {
- RAMBlock *block = ram_block_from_stream(f, flags);
+ RAMBlock *block = ram_block_from_stream(mis, f, flags);
host = host_from_ram_block_offset(block, addr);
/*
diff --git a/migration/rdma.c b/migration/rdma.c
index c7c7a38487..ef1e65ec36 100644
--- a/migration/rdma.c
+++ b/migration/rdma.c
@@ -2705,6 +2705,7 @@ static int qemu_rdma_dest_init(RDMAContext *rdma, Error **errp)
char ip[40] = "unknown";
struct rdma_addrinfo *res, *e;
char port_str[16];
+ int reuse = 1;
for (idx = 0; idx < RDMA_WRID_MAX; idx++) {
rdma->wr_data[idx].control_len = 0;
@@ -2740,6 +2741,12 @@ static int qemu_rdma_dest_init(RDMAContext *rdma, Error **errp)
goto err_dest_init_bind_addr;
}
+ ret = rdma_set_option(listen_id, RDMA_OPTION_ID, RDMA_OPTION_ID_REUSEADDR,
+ &reuse, sizeof reuse);
+ if (ret) {
+ ERROR(errp, "Error: could not set REUSEADDR option");
+ goto err_dest_init_bind_addr;
+ }
for (e = res; e != NULL; e = e->ai_next) {
inet_ntop(e->ai_family,
&((struct sockaddr_in *) e->ai_dst_addr)->sin_addr, ip, sizeof ip);
diff --git a/migration/savevm.c b/migration/savevm.c
index 1599b02fbc..967ff80547 100644
--- a/migration/savevm.c
+++ b/migration/savevm.c
@@ -1863,7 +1863,7 @@ static void *postcopy_ram_listen_thread(void *opaque)
migrate_set_state(&mis->state, MIGRATION_STATUS_ACTIVE,
MIGRATION_STATUS_POSTCOPY_ACTIVE);
- qemu_sem_post(&mis->listen_thread_sem);
+ qemu_sem_post(&mis->thread_sync_sem);
trace_postcopy_ram_listen_thread_start();
rcu_register_thread();
@@ -1948,9 +1948,10 @@ static void *postcopy_ram_listen_thread(void *opaque)
static int loadvm_postcopy_handle_listen(MigrationIncomingState *mis)
{
PostcopyState ps = postcopy_state_set(POSTCOPY_INCOMING_LISTENING);
- trace_loadvm_postcopy_handle_listen();
Error *local_err = NULL;
+ trace_loadvm_postcopy_handle_listen("enter");
+
if (ps != POSTCOPY_INCOMING_ADVISE && ps != POSTCOPY_INCOMING_DISCARD) {
error_report("CMD_POSTCOPY_LISTEN in wrong postcopy state (%d)", ps);
return -1;
@@ -1965,6 +1966,8 @@ static int loadvm_postcopy_handle_listen(MigrationIncomingState *mis)
}
}
+ trace_loadvm_postcopy_handle_listen("after discard");
+
/*
* Sensitise RAM - can now generate requests for blocks that don't exist
* However, at this point the CPU shouldn't be running, and the IO
@@ -1977,19 +1980,17 @@ static int loadvm_postcopy_handle_listen(MigrationIncomingState *mis)
}
}
+ trace_loadvm_postcopy_handle_listen("after uffd");
+
if (postcopy_notify(POSTCOPY_NOTIFY_INBOUND_LISTEN, &local_err)) {
error_report_err(local_err);
return -1;
}
mis->have_listen_thread = true;
- /* Start up the listening thread and wait for it to signal ready */
- qemu_sem_init(&mis->listen_thread_sem, 0);
- qemu_thread_create(&mis->listen_thread, "postcopy/listen",
- postcopy_ram_listen_thread, NULL,
- QEMU_THREAD_DETACHED);
- qemu_sem_wait(&mis->listen_thread_sem);
- qemu_sem_destroy(&mis->listen_thread_sem);
+ postcopy_thread_create(mis, &mis->listen_thread, "postcopy/listen",
+ postcopy_ram_listen_thread, QEMU_THREAD_DETACHED);
+ trace_loadvm_postcopy_handle_listen("return");
return 0;
}
@@ -1999,13 +2000,19 @@ static void loadvm_postcopy_handle_run_bh(void *opaque)
Error *local_err = NULL;
MigrationIncomingState *mis = opaque;
+ trace_loadvm_postcopy_handle_run_bh("enter");
+
/* TODO we should move all of this lot into postcopy_ram.c or a shared code
* in migration.c
*/
cpu_synchronize_all_post_init();
+ trace_loadvm_postcopy_handle_run_bh("after cpu sync");
+
qemu_announce_self(&mis->announce_timer, migrate_announce_params());
+ trace_loadvm_postcopy_handle_run_bh("after announce");
+
/* Make sure all file formats flush their mutable metadata.
* If we get an error here, just don't restart the VM yet. */
bdrv_invalidate_cache_all(&local_err);
@@ -2015,9 +2022,7 @@ static void loadvm_postcopy_handle_run_bh(void *opaque)
autostart = false;
}
- trace_loadvm_postcopy_handle_run_cpu_sync();
-
- trace_loadvm_postcopy_handle_run_vmstart();
+ trace_loadvm_postcopy_handle_run_bh("after invalidate cache");
dirty_bitmap_mig_before_vm_start();
@@ -2030,6 +2035,8 @@ static void loadvm_postcopy_handle_run_bh(void *opaque)
}
qemu_bh_delete(mis->bh);
+
+ trace_loadvm_postcopy_handle_run_bh("return");
}
/* After all discards we can start running and asking for pages */
@@ -2273,12 +2280,13 @@ static int loadvm_process_command(QEMUFile *f)
return qemu_file_get_error(f);
}
- trace_loadvm_process_command(cmd, len);
if (cmd >= MIG_CMD_MAX || cmd == MIG_CMD_INVALID) {
error_report("MIG_CMD 0x%x unknown (len 0x%x)", cmd, len);
return -EINVAL;
}
+ trace_loadvm_process_command(mig_cmd_args[cmd].name, len);
+
if (mig_cmd_args[cmd].len != -1 && mig_cmd_args[cmd].len != len) {
error_report("%s received with bad length - expecting %zu, got %d",
mig_cmd_args[cmd].name,
@@ -2565,6 +2573,18 @@ void qemu_loadvm_state_cleanup(void)
/* Return true if we should continue the migration, or false. */
static bool postcopy_pause_incoming(MigrationIncomingState *mis)
{
+ int i;
+
+ /*
+ * If network is interrupted, any temp page we received will be useless
+ * because we didn't mark them as "received" in receivedmap. After a
+ * proper recovery later (which will sync src dirty bitmap with receivedmap
+ * on dest) these cached small pages will be resent again.
+ */
+ for (i = 0; i < mis->postcopy_channels; i++) {
+ postcopy_temp_page_reset(&mis->postcopy_tmp_pages[i]);
+ }
+
trace_postcopy_pause_incoming();
assert(migrate_postcopy_ram());
diff --git a/migration/trace-events b/migration/trace-events
index 48aa7b10ee..1aec580e92 100644
--- a/migration/trace-events
+++ b/migration/trace-events
@@ -14,15 +14,14 @@ loadvm_handle_cmd_packaged_main(int ret) "%d"
loadvm_handle_cmd_packaged_received(int ret) "%d"
loadvm_handle_recv_bitmap(char *s) "%s"
loadvm_postcopy_handle_advise(void) ""
-loadvm_postcopy_handle_listen(void) ""
+loadvm_postcopy_handle_listen(const char *str) "%s"
loadvm_postcopy_handle_run(void) ""
-loadvm_postcopy_handle_run_cpu_sync(void) ""
-loadvm_postcopy_handle_run_vmstart(void) ""
+loadvm_postcopy_handle_run_bh(const char *str) "%s"
loadvm_postcopy_handle_resume(void) ""
loadvm_postcopy_ram_handle_discard(void) ""
loadvm_postcopy_ram_handle_discard_end(void) ""
loadvm_postcopy_ram_handle_discard_header(const char *ramid, uint16_t len) "%s: %ud"
-loadvm_process_command(uint16_t com, uint16_t len) "com=0x%x len=%d"
+loadvm_process_command(const char *s, uint16_t len) "com=%s len=%d"
loadvm_process_command_ping(uint32_t val) "0x%x"
postcopy_ram_listen_thread_exit(void) ""
postcopy_ram_listen_thread_start(void) ""
diff --git a/migration/vmstate.c b/migration/vmstate.c
index 05f87cdddc..36ae8b9e19 100644
--- a/migration/vmstate.c
+++ b/migration/vmstate.c
@@ -90,12 +90,6 @@ int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd,
return -EINVAL;
}
if (version_id < vmsd->minimum_version_id) {
- if (vmsd->load_state_old &&
- version_id >= vmsd->minimum_version_id_old) {
- ret = vmsd->load_state_old(f, opaque, version_id);
- trace_vmstate_load_state_end(vmsd->name, "old path", ret);
- return ret;
- }
error_report("%s: incoming version_id %d is too old "
"for local minimum version_id %d",
vmsd->name, version_id, vmsd->minimum_version_id);
diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c
index 8c384dc1b2..634968498b 100644
--- a/monitor/hmp-cmds.c
+++ b/monitor/hmp-cmds.c
@@ -1396,10 +1396,35 @@ void hmp_set_password(Monitor *mon, const QDict *qdict)
{
const char *protocol = qdict_get_str(qdict, "protocol");
const char *password = qdict_get_str(qdict, "password");
+ const char *display = qdict_get_try_str(qdict, "display");
const char *connected = qdict_get_try_str(qdict, "connected");
Error *err = NULL;
- qmp_set_password(protocol, password, !!connected, connected, &err);
+ SetPasswordOptions opts = {
+ .password = (char *)password,
+ .has_connected = !!connected,
+ };
+
+ opts.connected = qapi_enum_parse(&SetPasswordAction_lookup, connected,
+ SET_PASSWORD_ACTION_KEEP, &err);
+ if (err) {
+ goto out;
+ }
+
+ opts.protocol = qapi_enum_parse(&DisplayProtocol_lookup, protocol,
+ DISPLAY_PROTOCOL_VNC, &err);
+ if (err) {
+ goto out;
+ }
+
+ if (opts.protocol == DISPLAY_PROTOCOL_VNC) {
+ opts.u.vnc.has_display = !!display;
+ opts.u.vnc.display = (char *)display;
+ }
+
+ qmp_set_password(&opts, &err);
+
+out:
hmp_handle_error(mon, err);
}
@@ -1407,9 +1432,27 @@ void hmp_expire_password(Monitor *mon, const QDict *qdict)
{
const char *protocol = qdict_get_str(qdict, "protocol");
const char *whenstr = qdict_get_str(qdict, "time");
+ const char *display = qdict_get_try_str(qdict, "display");
Error *err = NULL;
- qmp_expire_password(protocol, whenstr, &err);
+ ExpirePasswordOptions opts = {
+ .time = (char *)whenstr,
+ };
+
+ opts.protocol = qapi_enum_parse(&DisplayProtocol_lookup, protocol,
+ DISPLAY_PROTOCOL_VNC, &err);
+ if (err) {
+ goto out;
+ }
+
+ if (opts.protocol == DISPLAY_PROTOCOL_VNC) {
+ opts.u.vnc.has_display = !!display;
+ opts.u.vnc.display = (char *)display;
+ }
+
+ qmp_expire_password(&opts, &err);
+
+out:
hmp_handle_error(mon, err);
}
diff --git a/monitor/hmp.c b/monitor/hmp.c
index b20737e63c..569066036d 100644
--- a/monitor/hmp.c
+++ b/monitor/hmp.c
@@ -981,6 +981,7 @@ static QDict *monitor_parse_arguments(Monitor *mon,
{
const char *tmp = p;
int skip_key = 0;
+ int ret;
/* option */
c = *typestr++;
@@ -1003,11 +1004,27 @@ static QDict *monitor_parse_arguments(Monitor *mon,
}
if (skip_key) {
p = tmp;
+ } else if (*typestr == 's') {
+ /* has option with string value */
+ typestr++;
+ tmp = p++;
+ while (qemu_isspace(*p)) {
+ p++;
+ }
+ ret = get_str(buf, sizeof(buf), &p);
+ if (ret < 0) {
+ monitor_printf(mon, "%s: value expected for -%c\n",
+ cmd->name, *tmp);
+ goto fail;
+ }
+ qdict_put_str(qdict, key, buf);
} else {
- /* has option */
+ /* has boolean option */
p++;
qdict_put_bool(qdict, key, true);
}
+ } else if (*typestr == 's') {
+ typestr++;
}
}
break;
diff --git a/monitor/monitor-internal.h b/monitor/monitor-internal.h
index 3da3f86c6a..caa2e90ef2 100644
--- a/monitor/monitor-internal.h
+++ b/monitor/monitor-internal.h
@@ -63,7 +63,8 @@
* '.' other form of optional type (for 'i' and 'l')
* 'b' boolean
* user mode accepts "on" or "off"
- * '-' optional parameter (eg. '-f')
+ * '-' optional parameter (eg. '-f'); if followed by a 's', it
+ * specifies an optional string param (e.g. '-fs' allows '-f foo')
*
*/
diff --git a/monitor/qmp-cmds.c b/monitor/qmp-cmds.c
index db4d186448..df97582dd4 100644
--- a/monitor/qmp-cmds.c
+++ b/monitor/qmp-cmds.c
@@ -168,45 +168,27 @@ void qmp_system_wakeup(Error **errp)
qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER, errp);
}
-void qmp_set_password(const char *protocol, const char *password,
- bool has_connected, const char *connected, Error **errp)
+void qmp_set_password(SetPasswordOptions *opts, Error **errp)
{
- int disconnect_if_connected = 0;
- int fail_if_connected = 0;
int rc;
- if (has_connected) {
- if (strcmp(connected, "fail") == 0) {
- fail_if_connected = 1;
- } else if (strcmp(connected, "disconnect") == 0) {
- disconnect_if_connected = 1;
- } else if (strcmp(connected, "keep") == 0) {
- /* nothing */
- } else {
- error_setg(errp, QERR_INVALID_PARAMETER, "connected");
- return;
- }
- }
-
- if (strcmp(protocol, "spice") == 0) {
+ if (opts->protocol == DISPLAY_PROTOCOL_SPICE) {
if (!qemu_using_spice(errp)) {
return;
}
- rc = qemu_spice.set_passwd(password, fail_if_connected,
- disconnect_if_connected);
- } else if (strcmp(protocol, "vnc") == 0) {
- if (fail_if_connected || disconnect_if_connected) {
+ rc = qemu_spice.set_passwd(opts->password,
+ opts->connected == SET_PASSWORD_ACTION_FAIL,
+ opts->connected == SET_PASSWORD_ACTION_DISCONNECT);
+ } else {
+ assert(opts->protocol == DISPLAY_PROTOCOL_VNC);
+ if (opts->connected != SET_PASSWORD_ACTION_KEEP) {
/* vnc supports "connected=keep" only */
error_setg(errp, QERR_INVALID_PARAMETER, "connected");
return;
}
/* Note that setting an empty password will not disable login through
* this interface. */
- rc = vnc_display_password(NULL, password);
- } else {
- error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "protocol",
- "'vnc' or 'spice'");
- return;
+ rc = vnc_display_password(opts->u.vnc.display, opts->password);
}
if (rc != 0) {
@@ -214,11 +196,11 @@ void qmp_set_password(const char *protocol, const char *password,
}
}
-void qmp_expire_password(const char *protocol, const char *whenstr,
- Error **errp)
+void qmp_expire_password(ExpirePasswordOptions *opts, Error **errp)
{
time_t when;
int rc;
+ const char *whenstr = opts->time;
if (strcmp(whenstr, "now") == 0) {
when = 0;
@@ -230,17 +212,14 @@ void qmp_expire_password(const char *protocol, const char *whenstr,
when = strtoull(whenstr, NULL, 10);
}
- if (strcmp(protocol, "spice") == 0) {
+ if (opts->protocol == DISPLAY_PROTOCOL_SPICE) {
if (!qemu_using_spice(errp)) {
return;
}
rc = qemu_spice.set_pw_expire(when);
- } else if (strcmp(protocol, "vnc") == 0) {
- rc = vnc_display_pw_expire(NULL, when);
} else {
- error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "protocol",
- "'vnc' or 'spice'");
- return;
+ assert(opts->protocol == DISPLAY_PROTOCOL_VNC);
+ rc = vnc_display_pw_expire(opts->u.vnc.display, when);
}
if (rc != 0) {
diff --git a/qapi/ui.json b/qapi/ui.json
index 9354f4c467..4a13f883a3 100644
--- a/qapi/ui.json
+++ b/qapi/ui.json
@@ -10,20 +10,75 @@
{ 'include': 'sockets.json' }
##
-# @set_password:
+# @DisplayProtocol:
+#
+# Display protocols which support changing password options.
+#
+# Since: 7.0
+#
+##
+{ 'enum': 'DisplayProtocol',
+ 'data': [ 'vnc', 'spice' ] }
+
+##
+# @SetPasswordAction:
+#
+# An action to take on changing a password on a connection with active clients.
+#
+# @keep: maintain existing clients
+#
+# @fail: fail the command if clients are connected
+#
+# @disconnect: disconnect existing clients
+#
+# Since: 7.0
+#
+##
+{ 'enum': 'SetPasswordAction',
+ 'data': [ 'keep', 'fail', 'disconnect' ] }
+
+##
+# @SetPasswordOptions:
#
-# Sets the password of a remote display session.
+# Options for set_password.
#
# @protocol: - 'vnc' to modify the VNC server password
# - 'spice' to modify the Spice server password
#
# @password: the new password
#
-# @connected: how to handle existing clients when changing the
-# password. If nothing is specified, defaults to 'keep'
-# 'fail' to fail the command if clients are connected
-# 'disconnect' to disconnect existing clients
-# 'keep' to maintain existing clients
+# @connected: How to handle existing clients when changing the
+# password. If nothing is specified, defaults to 'keep'.
+# For VNC, only 'keep' is currently implemented.
+#
+# Since: 7.0
+#
+##
+{ 'union': 'SetPasswordOptions',
+ 'base': { 'protocol': 'DisplayProtocol',
+ 'password': 'str',
+ '*connected': 'SetPasswordAction' },
+ 'discriminator': 'protocol',
+ 'data': { 'vnc': 'SetPasswordOptionsVnc' } }
+
+##
+# @SetPasswordOptionsVnc:
+#
+# Options for set_password specific to the VNC procotol.
+#
+# @display: The id of the display where the password should be changed.
+# Defaults to the first.
+#
+# Since: 7.0
+#
+##
+{ 'struct': 'SetPasswordOptionsVnc',
+ 'data': { '*display': 'str' } }
+
+##
+# @set_password:
+#
+# Set the password of a remote display server.
#
# Returns: - Nothing on success
# - If Spice is not enabled, DeviceNotFound
@@ -37,15 +92,15 @@
# <- { "return": {} }
#
##
-{ 'command': 'set_password',
- 'data': {'protocol': 'str', 'password': 'str', '*connected': 'str'} }
+{ 'command': 'set_password', 'boxed': true, 'data': 'SetPasswordOptions' }
##
-# @expire_password:
+# @ExpirePasswordOptions:
#
-# Expire the password of a remote display server.
+# General options for expire_password.
#
-# @protocol: the name of the remote display protocol 'vnc' or 'spice'
+# @protocol: - 'vnc' to modify the VNC server expiration
+# - 'spice' to modify the Spice server expiration
#
# @time: when to expire the password.
#
@@ -54,16 +109,45 @@
# - '+INT' where INT is the number of seconds from now (integer)
# - 'INT' where INT is the absolute time in seconds
#
-# Returns: - Nothing on success
-# - If @protocol is 'spice' and Spice is not active, DeviceNotFound
-#
-# Since: 0.14
-#
# Notes: Time is relative to the server and currently there is no way to
# coordinate server time with client time. It is not recommended to
# use the absolute time version of the @time parameter unless you're
# sure you are on the same machine as the QEMU instance.
#
+# Since: 7.0
+#
+##
+{ 'union': 'ExpirePasswordOptions',
+ 'base': { 'protocol': 'DisplayProtocol',
+ 'time': 'str' },
+ 'discriminator': 'protocol',
+ 'data': { 'vnc': 'ExpirePasswordOptionsVnc' } }
+
+##
+# @ExpirePasswordOptionsVnc:
+#
+# Options for expire_password specific to the VNC procotol.
+#
+# @display: The id of the display where the expiration should be changed.
+# Defaults to the first.
+#
+# Since: 7.0
+#
+##
+
+{ 'struct': 'ExpirePasswordOptionsVnc',
+ 'data': { '*display': 'str' } }
+
+##
+# @expire_password:
+#
+# Expire the password of a remote display server.
+#
+# Returns: - Nothing on success
+# - If @protocol is 'spice' and Spice is not active, DeviceNotFound
+#
+# Since: 0.14
+#
# Example:
#
# -> { "execute": "expire_password", "arguments": { "protocol": "vnc",
@@ -71,7 +155,7 @@
# <- { "return": {} }
#
##
-{ 'command': 'expire_password', 'data': {'protocol': 'str', 'time': 'str'} }
+{ 'command': 'expire_password', 'boxed': true, 'data': 'ExpirePasswordOptions' }
##
# @screendump:
diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c
index 7b42f6fd90..0870656d82 100644
--- a/tests/qtest/migration-test.c
+++ b/tests/qtest/migration-test.c
@@ -495,7 +495,7 @@ static void migrate_start_destroy(MigrateStart *args)
}
static int test_migrate_start(QTestState **from, QTestState **to,
- const char *uri, MigrateStart *args)
+ const char *uri, MigrateStart **pargs)
{
g_autofree gchar *arch_source = NULL;
g_autofree gchar *arch_target = NULL;
@@ -507,6 +507,7 @@ static int test_migrate_start(QTestState **from, QTestState **to,
g_autofree char *shmem_path = NULL;
const char *arch = qtest_get_arch();
const char *machine_opts = NULL;
+ MigrateStart *args = *pargs;
const char *memory_size;
int ret = 0;
@@ -621,6 +622,8 @@ static int test_migrate_start(QTestState **from, QTestState **to,
out:
migrate_start_destroy(args);
+ /* This tells the caller that this structure is gone */
+ *pargs = NULL;
return ret;
}
@@ -665,7 +668,7 @@ static int migrate_postcopy_prepare(QTestState **from_ptr,
g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs);
QTestState *from, *to;
- if (test_migrate_start(&from, &to, uri, args)) {
+ if (test_migrate_start(&from, &to, uri, &args)) {
return -1;
}
@@ -788,7 +791,7 @@ static void test_baddest(void)
args->hide_stderr = true;
- if (test_migrate_start(&from, &to, "tcp:127.0.0.1:0", args)) {
+ if (test_migrate_start(&from, &to, "tcp:127.0.0.1:0", &args)) {
return;
}
migrate_qmp(from, "tcp:127.0.0.1:0", "{}");
@@ -804,7 +807,7 @@ static void test_precopy_unix_common(bool dirty_ring)
args->use_dirty_ring = dirty_ring;
- if (test_migrate_start(&from, &to, uri, args)) {
+ if (test_migrate_start(&from, &to, uri, &args)) {
return;
}
@@ -892,7 +895,7 @@ static void test_xbzrle(const char *uri)
MigrateStart *args = migrate_start_new();
QTestState *from, *to;
- if (test_migrate_start(&from, &to, uri, args)) {
+ if (test_migrate_start(&from, &to, uri, &args)) {
return;
}
@@ -946,7 +949,7 @@ static void test_precopy_tcp(void)
g_autofree char *uri = NULL;
QTestState *from, *to;
- if (test_migrate_start(&from, &to, "tcp:127.0.0.1:0", args)) {
+ if (test_migrate_start(&from, &to, "tcp:127.0.0.1:0", &args)) {
return;
}
@@ -991,7 +994,7 @@ static void test_migrate_fd_proto(void)
QDict *rsp;
const char *error_desc;
- if (test_migrate_start(&from, &to, "defer", args)) {
+ if (test_migrate_start(&from, &to, "defer", &args)) {
return;
}
@@ -1071,7 +1074,7 @@ static void do_test_validate_uuid(MigrateStart *args, bool should_fail)
g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs);
QTestState *from, *to;
- if (test_migrate_start(&from, &to, uri, args)) {
+ if (test_migrate_start(&from, &to, uri, &args)) {
return;
}
@@ -1163,7 +1166,7 @@ static void test_migrate_auto_converge(void)
*/
const int64_t expected_threshold = max_bandwidth * downtime_limit / 1000;
- if (test_migrate_start(&from, &to, uri, args)) {
+ if (test_migrate_start(&from, &to, uri, &args)) {
return;
}
@@ -1232,7 +1235,7 @@ static void test_multifd_tcp(const char *method)
QDict *rsp;
g_autofree char *uri = NULL;
- if (test_migrate_start(&from, &to, "defer", args)) {
+ if (test_migrate_start(&from, &to, "defer", &args)) {
return;
}
@@ -1318,7 +1321,7 @@ static void test_multifd_tcp_cancel(void)
args->hide_stderr = true;
- if (test_migrate_start(&from, &to, "defer", args)) {
+ if (test_migrate_start(&from, &to, "defer", &args)) {
return;
}
@@ -1357,7 +1360,7 @@ static void test_multifd_tcp_cancel(void)
args = migrate_start_new();
args->only_target = true;
- if (test_migrate_start(&from, &to2, "defer", args)) {
+ if (test_migrate_start(&from, &to2, "defer", &args)) {
return;
}
diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c
index dfa2fc250d..028dacdd8f 100644
--- a/tools/virtiofsd/passthrough_ll.c
+++ b/tools/virtiofsd/passthrough_ll.c
@@ -1039,7 +1039,7 @@ static int do_statx(struct lo_data *lo, int dirfd, const char *pathname,
{
int res;
-#if defined(CONFIG_STATX) && defined(STATX_MNT_ID)
+#if defined(CONFIG_STATX) && defined(CONFIG_STATX_MNT_ID)
if (lo->use_statx) {
struct statx statxbuf;