mirror of
https://gitlab.com/qemu-project/qemu
synced 2024-11-05 20:35:44 +00:00
Migration/HMP/Virtio pull 2022-03-02
A bit of a mix this time: * Minor fixes from myself, Hanna, and Jack * VNC password rework by Stefan and Fabian * Postcopy changes from Peter X that are the start of a larger series to come * Removing the prehistoic load_state_old code from Peter M Signed-off-by: Dr. David Alan Gilbert <dgilbert@redhat.com> -----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEERfXHG0oMt/uXep+pBRYzHrxb/ecFAmIftogACgkQBRYzHrxb /ed19xAApta5EZm7X28mDz6uWKK0Rwwgwn195QGPz3LR/VbG69iYRAhVsD417u6j D1T4E5l+uY8T6gSEMPsEd3NYF79Au0HkE7Oo2YDZLKheWOIt+bsGUUDp6SpNPm85 +MiwfqjN+yhzexrck1MSfOGZyGSv9EF+4iYODOA0Q79x8E4vztgCqJqjUDjm1j7S DrR10znaXNNAdC9ZtkacwF8q9LglzQLWJmkNfSLTp3IJVSeLqokSG2aFM0tLA1rA EGqJKUOtULcyzYxTt+V4FTAFOea4P14riAcMWEpQGdU7td7fKOmjcSJoKEDA5pdI wHzSQ3jxm4iXFrfIJAz5r9EzNoRaiMYO1AyApAnFXr638mDHdeIWCKfwOC08pS7+ Bq1+bdHqkFQUpjHUfrcEnFCs/KD6rEmLahJCHDzMzaHxxxEKpZyXSVT51TVI3X/5 HKrz4xeGetQHLPi1jV9157fJHzGFTTpMVbr77wcc+KqJ1KhAJT+neIyaeDlMvIX7 iw219JkavhVqK6NXvwo5m75wr4kVyuUfw6HaXn2y9NWe75lo4uuCoRGz8bd/pXZK grBSxTTwZUItQtG+x3CIuqfAByyvAtV454cuQLyusR4P3GbDi3DwK6MIsa9qfVS5 FV3F0FWXxPPGa5NuRRvTY8f7WyKziq5y/DRckUTB1QH3ct2GTdk= =L5uY -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/dgilbert-gitlab/tags/pull-migration-20220302b' into staging Migration/HMP/Virtio pull 2022-03-02 A bit of a mix this time: * Minor fixes from myself, Hanna, and Jack * VNC password rework by Stefan and Fabian * Postcopy changes from Peter X that are the start of a larger series to come * Removing the prehistoic load_state_old code from Peter M Signed-off-by: Dr. David Alan Gilbert <dgilbert@redhat.com> # gpg: Signature made Wed 02 Mar 2022 18:25:12 GMT # gpg: using RSA key 45F5C71B4A0CB7FB977A9FA90516331EBC5BFDE7 # gpg: Good signature from "Dr. David Alan Gilbert (RH2) <dgilbert@redhat.com>" [full] # Primary key fingerprint: 45F5 C71B 4A0C B7FB 977A 9FA9 0516 331E BC5B FDE7 * remotes/dgilbert-gitlab/tags/pull-migration-20220302b: migration: Remove load_state_old and minimum_version_id_old tests: Pass in MigrateStart** into test_migrate_start() migration: Add migration_incoming_transport_cleanup() migration: postcopy_pause_fault_thread() never fails migration: Enlarge postcopy recovery to capture !-EIO too migration: Move static var in ram_block_from_stream() into global migration: Add postcopy_thread_create() migration: Dump ramblock and offset too when non-same-page detected migration: Introduce postcopy channels on dest node migration: Tracepoint change in postcopy-run bottom half migration: Finer grained tracepoints for POSTCOPY_LISTEN migration: Dump sub-cmd name in loadvm_process_command tp migration/rdma: set the REUSEADDR option for destination qapi/monitor: allow VNC display id in set/expire_password qapi/monitor: refactor set/expire_password with enums monitor/hmp: add support for flag argument with value virtiofsd: Let meson check for statx.stx_mnt_id clock-vmstate: Add missing END_OF_LIST Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
36eae3a732
22 changed files with 433 additions and 199 deletions
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -44,6 +44,7 @@ const VMStateDescription vmstate_muldiv = {
|
|||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT32(multiplier, Clock),
|
||||
VMSTATE_UINT32(divider, Clock),
|
||||
VMSTATE_END_OF_LIST()
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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);
|
||||
|
|
13
meson.build
13
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())
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
PostcopyTmpPage *tmp_page;
|
||||
int err, i, channels;
|
||||
void *temp_page;
|
||||
|
||||
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;
|
||||
/* 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)
|
||||
{
|
||||
|
|
|
@ -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! */
|
||||
|
|
|
@ -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);
|
||||
/*
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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) ""
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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')
|
||||
*
|
||||
*/
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
120
qapi/ui.json
120
qapi/ui.json
|
@ -10,20 +10,75 @@
|
|||
{ 'include': 'sockets.json' }
|
||||
|
||||
##
|
||||
# @set_password:
|
||||
# @DisplayProtocol:
|
||||
#
|
||||
# Sets the password of a remote display session.
|
||||
# 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:
|
||||
#
|
||||
# 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:
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
Loading…
Reference in a new issue