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
@ -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
|
the state as the newer version. But ``load_state`` sometimes is able to
|
||||||
load state from an older version.
|
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.
|
- ``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
|
- ``minimum_version_id``: the minimum version_id that VMState is able to understand
|
||||||
for that device.
|
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
|
VMState is able to read versions from minimum_version_id to version_id.
|
||||||
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.
|
|
||||||
|
|
||||||
There are *_V* forms of many ``VMSTATE_`` macros to load fields for version dependent fields,
|
There are *_V* forms of many ``VMSTATE_`` macros to load fields for version dependent fields,
|
||||||
e.g.
|
e.g.
|
||||||
|
@ -1514,33 +1514,35 @@ ERST
|
|||||||
|
|
||||||
{
|
{
|
||||||
.name = "set_password",
|
.name = "set_password",
|
||||||
.args_type = "protocol:s,password:s,connected:s?",
|
.args_type = "protocol:s,password:s,display:-ds,connected:s?",
|
||||||
.params = "protocol password action-if-connected",
|
.params = "protocol password [-d display] [action-if-connected]",
|
||||||
.help = "set spice/vnc password",
|
.help = "set spice/vnc password",
|
||||||
.cmd = hmp_set_password,
|
.cmd = hmp_set_password,
|
||||||
},
|
},
|
||||||
|
|
||||||
SRST
|
SRST
|
||||||
``set_password [ vnc | spice ] password [ action-if-connected ]``
|
``set_password [ vnc | spice ] password [ -d display ] [ action-if-connected ]``
|
||||||
Change spice/vnc password. *action-if-connected* specifies what
|
Change spice/vnc password. *display* can be used with 'vnc' to specify
|
||||||
should happen in case a connection is established: *fail* makes the
|
which display to set the password on. *action-if-connected* specifies
|
||||||
password change fail. *disconnect* changes the password and
|
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
|
disconnects the client. *keep* changes the password and keeps the
|
||||||
connection up. *keep* is the default.
|
connection up. *keep* is the default.
|
||||||
ERST
|
ERST
|
||||||
|
|
||||||
{
|
{
|
||||||
.name = "expire_password",
|
.name = "expire_password",
|
||||||
.args_type = "protocol:s,time:s",
|
.args_type = "protocol:s,time:s,display:-ds",
|
||||||
.params = "protocol time",
|
.params = "protocol time [-d display]",
|
||||||
.help = "set spice/vnc password expire-time",
|
.help = "set spice/vnc password expire-time",
|
||||||
.cmd = hmp_expire_password,
|
.cmd = hmp_expire_password,
|
||||||
},
|
},
|
||||||
|
|
||||||
SRST
|
SRST
|
||||||
``expire_password [ vnc | spice ]`` *expire-time*
|
``expire_password [ vnc | spice ] expire-time [ -d display ]``
|
||||||
Specify when a password for spice/vnc becomes
|
Specify when a password for spice/vnc becomes invalid.
|
||||||
invalid. *expire-time* accepts:
|
*display* behaves the same as in ``set_password``.
|
||||||
|
*expire-time* accepts:
|
||||||
|
|
||||||
``now``
|
``now``
|
||||||
Invalidate password instantly.
|
Invalidate password instantly.
|
||||||
|
@ -44,6 +44,7 @@ const VMStateDescription vmstate_muldiv = {
|
|||||||
.fields = (VMStateField[]) {
|
.fields = (VMStateField[]) {
|
||||||
VMSTATE_UINT32(multiplier, Clock),
|
VMSTATE_UINT32(multiplier, Clock),
|
||||||
VMSTATE_UINT32(divider, 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,
|
.name = TYPE_XILINX_VERSAL_OSPI,
|
||||||
.version_id = 1,
|
.version_id = 1,
|
||||||
.minimum_version_id = 1,
|
.minimum_version_id = 1,
|
||||||
.minimum_version_id_old = 1,
|
|
||||||
.fields = (VMStateField[]) {
|
.fields = (VMStateField[]) {
|
||||||
VMSTATE_FIFO8(rx_fifo, XlnxVersalOspi),
|
VMSTATE_FIFO8(rx_fifo, XlnxVersalOspi),
|
||||||
VMSTATE_FIFO8(tx_fifo, XlnxVersalOspi),
|
VMSTATE_FIFO8(tx_fifo, XlnxVersalOspi),
|
||||||
|
@ -181,9 +181,7 @@ struct VMStateDescription {
|
|||||||
int unmigratable;
|
int unmigratable;
|
||||||
int version_id;
|
int version_id;
|
||||||
int minimum_version_id;
|
int minimum_version_id;
|
||||||
int minimum_version_id_old;
|
|
||||||
MigrationPriority priority;
|
MigrationPriority priority;
|
||||||
LoadStateHandler *load_state_old;
|
|
||||||
int (*pre_load)(void *opaque);
|
int (*pre_load)(void *opaque);
|
||||||
int (*post_load)(void *opaque, int version_id);
|
int (*post_load)(void *opaque, int version_id);
|
||||||
int (*pre_save)(void *opaque);
|
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)
|
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') \
|
have_vhost_user_blk_server = get_option('vhost_user_blk_server') \
|
||||||
.require(targetos == 'linux',
|
.require(targetos == 'linux',
|
||||||
error_message: 'vhost_user_blk_server requires 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_QEMU_PRIVATE_XTS', xts == 'private')
|
||||||
config_host_data.set('CONFIG_MALLOC_TRIM', has_malloc_trim)
|
config_host_data.set('CONFIG_MALLOC_TRIM', has_malloc_trim)
|
||||||
config_host_data.set('CONFIG_STATX', has_statx)
|
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_ZSTD', zstd.found())
|
||||||
config_host_data.set('CONFIG_FUSE', fuse.found())
|
config_host_data.set('CONFIG_FUSE', fuse.found())
|
||||||
config_host_data.set('CONFIG_FUSE_LSEEK', fuse_lseek.found())
|
config_host_data.set('CONFIG_FUSE_LSEEK', fuse_lseek.found())
|
||||||
|
@ -267,6 +267,19 @@ MigrationIncomingState *migration_incoming_get_current(void)
|
|||||||
return current_incoming;
|
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)
|
void migration_incoming_state_destroy(void)
|
||||||
{
|
{
|
||||||
struct MigrationIncomingState *mis = migration_incoming_get_current();
|
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);
|
g_array_free(mis->postcopy_remote_fds, TRUE);
|
||||||
mis->postcopy_remote_fds = NULL;
|
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);
|
qemu_event_reset(&mis->main_thread_load_event);
|
||||||
|
|
||||||
if (mis->page_requested) {
|
if (mis->page_requested) {
|
||||||
@ -298,11 +309,6 @@ void migration_incoming_state_destroy(void)
|
|||||||
mis->page_requested = NULL;
|
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);
|
yank_unregister_instance(MIGRATION_YANK_INSTANCE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2865,7 +2871,7 @@ retry:
|
|||||||
out:
|
out:
|
||||||
res = qemu_file_get_error(rp);
|
res = qemu_file_get_error(rp);
|
||||||
if (res) {
|
if (res) {
|
||||||
if (res == -EIO && migration_in_postcopy()) {
|
if (res && migration_in_postcopy()) {
|
||||||
/*
|
/*
|
||||||
* Maybe there is something we can do: it looks like a
|
* Maybe there is something we can do: it looks like a
|
||||||
* network down issue, and we pause for a recovery.
|
* network down issue, and we pause for a recovery.
|
||||||
@ -3466,7 +3472,7 @@ static MigThrError migration_detect_error(MigrationState *s)
|
|||||||
error_free(local_error);
|
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
|
* For postcopy, we allow the network to be down for a
|
||||||
* while. After that, it can be continued by a
|
* while. After that, it can be continued by a
|
||||||
|
@ -45,14 +45,37 @@ struct PostcopyBlocktimeContext;
|
|||||||
*/
|
*/
|
||||||
#define CLEAR_BITMAP_SHIFT_MAX 31
|
#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 */
|
/* State for the incoming migration */
|
||||||
struct MigrationIncomingState {
|
struct MigrationIncomingState {
|
||||||
QEMUFile *from_src_file;
|
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 */
|
/* A hook to allow cleanup at the end of incoming migration */
|
||||||
void *transport_data;
|
void *transport_data;
|
||||||
void (*transport_cleanup)(void *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
|
* Free at the start of the main state load, set as the main thread finishes
|
||||||
* loading state.
|
* loading state.
|
||||||
@ -65,13 +88,11 @@ struct MigrationIncomingState {
|
|||||||
size_t largest_page_size;
|
size_t largest_page_size;
|
||||||
bool have_fault_thread;
|
bool have_fault_thread;
|
||||||
QemuThread fault_thread;
|
QemuThread fault_thread;
|
||||||
QemuSemaphore fault_thread_sem;
|
|
||||||
/* Set this when we want the fault thread to quit */
|
/* Set this when we want the fault thread to quit */
|
||||||
bool fault_thread_quit;
|
bool fault_thread_quit;
|
||||||
|
|
||||||
bool have_listen_thread;
|
bool have_listen_thread;
|
||||||
QemuThread listen_thread;
|
QemuThread listen_thread;
|
||||||
QemuSemaphore listen_thread_sem;
|
|
||||||
|
|
||||||
/* For the kernel to send us notifications */
|
/* For the kernel to send us notifications */
|
||||||
int userfault_fd;
|
int userfault_fd;
|
||||||
@ -81,7 +102,22 @@ struct MigrationIncomingState {
|
|||||||
QemuMutex rp_mutex; /* We send replies from multiple threads */
|
QemuMutex rp_mutex; /* We send replies from multiple threads */
|
||||||
/* RAMBlock of last request sent to source */
|
/* RAMBlock of last request sent to source */
|
||||||
RAMBlock *last_rb;
|
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;
|
void *postcopy_tmp_zero_page;
|
||||||
/* PostCopyFD's for external userfaultfds & handlers of shared memory */
|
/* PostCopyFD's for external userfaultfds & handlers of shared memory */
|
||||||
GArray *postcopy_remote_fds;
|
GArray *postcopy_remote_fds;
|
||||||
@ -130,6 +166,7 @@ struct MigrationIncomingState {
|
|||||||
|
|
||||||
MigrationIncomingState *migration_incoming_get_current(void);
|
MigrationIncomingState *migration_incoming_get_current(void);
|
||||||
void migration_incoming_state_destroy(void);
|
void migration_incoming_state_destroy(void);
|
||||||
|
void migration_incoming_transport_cleanup(MigrationIncomingState *mis);
|
||||||
/*
|
/*
|
||||||
* Functions to work with blocktime context
|
* Functions to work with blocktime context
|
||||||
*/
|
*/
|
||||||
@ -391,5 +428,6 @@ bool migration_rate_limit(void);
|
|||||||
void migration_cancel(const Error *error);
|
void migration_cancel(const Error *error);
|
||||||
|
|
||||||
void populate_vfio_info(MigrationInfo *info);
|
void populate_vfio_info(MigrationInfo *info);
|
||||||
|
void postcopy_temp_page_reset(PostcopyTmpPage *tmp_page);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -78,6 +78,20 @@ int postcopy_notify(enum PostcopyNotifyReason reason, Error **errp)
|
|||||||
&pnd);
|
&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
|
/* 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
|
* across, and efficiently map new pages in, the techniques for doing this
|
||||||
* are target OS specific.
|
* are target OS specific.
|
||||||
@ -526,9 +540,18 @@ int postcopy_ram_incoming_init(MigrationIncomingState *mis)
|
|||||||
|
|
||||||
static void postcopy_temp_pages_cleanup(MigrationIncomingState *mis)
|
static void postcopy_temp_pages_cleanup(MigrationIncomingState *mis)
|
||||||
{
|
{
|
||||||
if (mis->postcopy_tmp_page) {
|
int i;
|
||||||
munmap(mis->postcopy_tmp_page, mis->largest_page_size);
|
|
||||||
mis->postcopy_tmp_page = NULL;
|
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) {
|
if (mis->postcopy_tmp_zero_page) {
|
||||||
@ -868,15 +891,11 @@ static void mark_postcopy_blocktime_end(uintptr_t addr)
|
|||||||
affected_cpu);
|
affected_cpu);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool postcopy_pause_fault_thread(MigrationIncomingState *mis)
|
static void postcopy_pause_fault_thread(MigrationIncomingState *mis)
|
||||||
{
|
{
|
||||||
trace_postcopy_pause_fault_thread();
|
trace_postcopy_pause_fault_thread();
|
||||||
|
|
||||||
qemu_sem_wait(&mis->postcopy_pause_sem_fault);
|
qemu_sem_wait(&mis->postcopy_pause_sem_fault);
|
||||||
|
|
||||||
trace_postcopy_pause_fault_thread_continued();
|
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();
|
trace_postcopy_ram_fault_thread_entry();
|
||||||
rcu_register_thread();
|
rcu_register_thread();
|
||||||
mis->last_rb = NULL; /* last RAMBlock we sent part of */
|
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;
|
struct pollfd *pfd;
|
||||||
size_t pfd_len = 2 + mis->postcopy_remote_fds->len;
|
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
|
* broken already using the event. We should hold until
|
||||||
* the channel is rebuilt.
|
* the channel is rebuilt.
|
||||||
*/
|
*/
|
||||||
if (postcopy_pause_fault_thread(mis)) {
|
postcopy_pause_fault_thread(mis);
|
||||||
/* Continue to read the userfaultfd */
|
|
||||||
} else {
|
|
||||||
error_report("%s: paused but don't allow to continue",
|
|
||||||
__func__);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pfd[1].revents) {
|
if (pfd[1].revents) {
|
||||||
@ -1016,15 +1029,8 @@ retry:
|
|||||||
msg.arg.pagefault.address);
|
msg.arg.pagefault.address);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
/* May be network failure, try to wait for recovery */
|
/* May be network failure, try to wait for recovery */
|
||||||
if (ret == -EIO && postcopy_pause_fault_thread(mis)) {
|
postcopy_pause_fault_thread(mis);
|
||||||
/* We got reconnected somehow, try to continue */
|
goto retry;
|
||||||
goto retry;
|
|
||||||
} else {
|
|
||||||
/* This is a unavoidable fault */
|
|
||||||
error_report("%s: postcopy_request_page() get %d",
|
|
||||||
__func__, ret);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1092,17 +1098,30 @@ retry:
|
|||||||
|
|
||||||
static int postcopy_temp_pages_setup(MigrationIncomingState *mis)
|
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,
|
/* TODO: will be boosted when enable postcopy preemption */
|
||||||
PROT_READ | PROT_WRITE,
|
mis->postcopy_channels = 1;
|
||||||
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
|
||||||
if (mis->postcopy_tmp_page == MAP_FAILED) {
|
channels = mis->postcopy_channels;
|
||||||
err = errno;
|
mis->postcopy_tmp_pages = g_malloc0_n(sizeof(PostcopyTmpPage), channels);
|
||||||
mis->postcopy_tmp_page = NULL;
|
|
||||||
error_report("%s: Failed to map postcopy_tmp_page %s",
|
for (i = 0; i < channels; i++) {
|
||||||
__func__, strerror(err));
|
tmp_page = &mis->postcopy_tmp_pages[i];
|
||||||
return -err;
|
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;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
qemu_sem_init(&mis->fault_thread_sem, 0);
|
postcopy_thread_create(mis, &mis->fault_thread, "postcopy/fault",
|
||||||
qemu_thread_create(&mis->fault_thread, "postcopy/fault",
|
postcopy_ram_fault_thread, QEMU_THREAD_JOINABLE);
|
||||||
postcopy_ram_fault_thread, mis, QEMU_THREAD_JOINABLE);
|
|
||||||
qemu_sem_wait(&mis->fault_thread_sem);
|
|
||||||
qemu_sem_destroy(&mis->fault_thread_sem);
|
|
||||||
mis->have_fault_thread = true;
|
mis->have_fault_thread = true;
|
||||||
|
|
||||||
/* Mark so that we get notified of accesses to unwritten areas */
|
/* Mark so that we get notified of accesses to unwritten areas */
|
||||||
@ -1352,6 +1368,16 @@ int postcopy_wake_shared(struct PostCopyFD *pcfd,
|
|||||||
#endif
|
#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)
|
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 */
|
/* Call the notifier list set by postcopy_add_start_notifier */
|
||||||
int postcopy_notify(enum PostcopyNotifyReason reason, Error **errp);
|
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;
|
struct PostCopyFD;
|
||||||
|
|
||||||
/* ufd is a pointer to the struct uffd_msg *TODO: more Portable! */
|
/* 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.
|
* Returns a pointer from within the RCU-protected ram_list.
|
||||||
*
|
*
|
||||||
|
* @mis: the migration incoming state pointer
|
||||||
* @f: QEMUFile where to read the data from
|
* @f: QEMUFile where to read the data from
|
||||||
* @flags: Page flags (mostly to see if it's a continuation of previous block)
|
* @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];
|
char id[256];
|
||||||
uint8_t len;
|
uint8_t len;
|
||||||
|
|
||||||
@ -3217,6 +3219,8 @@ static inline RAMBlock *ram_block_from_stream(QEMUFile *f, int flags)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mis->last_recv_block = block;
|
||||||
|
|
||||||
return block;
|
return block;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3641,11 +3645,8 @@ static int ram_load_postcopy(QEMUFile *f)
|
|||||||
bool place_needed = false;
|
bool place_needed = false;
|
||||||
bool matches_target_page_size = false;
|
bool matches_target_page_size = false;
|
||||||
MigrationIncomingState *mis = migration_incoming_get_current();
|
MigrationIncomingState *mis = migration_incoming_get_current();
|
||||||
/* Temporary page that is later 'placed' */
|
/* Currently we only use channel 0. TODO: use all the channels */
|
||||||
void *postcopy_host_page = mis->postcopy_tmp_page;
|
PostcopyTmpPage *tmp_page = &mis->postcopy_tmp_pages[0];
|
||||||
void *host_page = NULL;
|
|
||||||
bool all_zero = true;
|
|
||||||
int target_pages = 0;
|
|
||||||
|
|
||||||
while (!ret && !(flags & RAM_SAVE_FLAG_EOS)) {
|
while (!ret && !(flags & RAM_SAVE_FLAG_EOS)) {
|
||||||
ram_addr_t addr;
|
ram_addr_t addr;
|
||||||
@ -3672,7 +3673,7 @@ static int ram_load_postcopy(QEMUFile *f)
|
|||||||
trace_ram_load_postcopy_loop((uint64_t)addr, flags);
|
trace_ram_load_postcopy_loop((uint64_t)addr, flags);
|
||||||
if (flags & (RAM_SAVE_FLAG_ZERO | RAM_SAVE_FLAG_PAGE |
|
if (flags & (RAM_SAVE_FLAG_ZERO | RAM_SAVE_FLAG_PAGE |
|
||||||
RAM_SAVE_FLAG_COMPRESS_PAGE)) {
|
RAM_SAVE_FLAG_COMPRESS_PAGE)) {
|
||||||
block = ram_block_from_stream(f, flags);
|
block = ram_block_from_stream(mis, f, flags);
|
||||||
if (!block) {
|
if (!block) {
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
break;
|
break;
|
||||||
@ -3689,7 +3690,7 @@ static int ram_load_postcopy(QEMUFile *f)
|
|||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
target_pages++;
|
tmp_page->target_pages++;
|
||||||
matches_target_page_size = block->page_size == TARGET_PAGE_SIZE;
|
matches_target_page_size = block->page_size == TARGET_PAGE_SIZE;
|
||||||
/*
|
/*
|
||||||
* Postcopy requires that we place whole host pages atomically;
|
* 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
|
* however the source ensures it always sends all the components
|
||||||
* of a host page in one chunk.
|
* 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);
|
host_page_offset_from_ram_block_offset(block, addr);
|
||||||
/* If all TP are zero then we can optimise the place */
|
/* If all TP are zero then we can optimise the place */
|
||||||
if (target_pages == 1) {
|
if (tmp_page->target_pages == 1) {
|
||||||
host_page = host_page_from_ram_block_offset(block, addr);
|
tmp_page->host_addr =
|
||||||
} else if (host_page != host_page_from_ram_block_offset(block,
|
host_page_from_ram_block_offset(block, addr);
|
||||||
addr)) {
|
} else if (tmp_page->host_addr !=
|
||||||
|
host_page_from_ram_block_offset(block, addr)) {
|
||||||
/* not the 1st TP within the HP */
|
/* not the 1st TP within the HP */
|
||||||
error_report("Non-same host page %p/%p", host_page,
|
error_report("Non-same host page detected. "
|
||||||
host_page_from_ram_block_offset(block, addr));
|
"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;
|
ret = -EINVAL;
|
||||||
break;
|
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
|
* If it's the last part of a host page then we place the host
|
||||||
* page
|
* 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_needed = true;
|
||||||
}
|
}
|
||||||
place_source = postcopy_host_page;
|
place_source = tmp_page->tmp_huge_page;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (flags & ~RAM_SAVE_FLAG_CONTINUE) {
|
switch (flags & ~RAM_SAVE_FLAG_CONTINUE) {
|
||||||
@ -3736,12 +3743,12 @@ static int ram_load_postcopy(QEMUFile *f)
|
|||||||
memset(page_buffer, ch, TARGET_PAGE_SIZE);
|
memset(page_buffer, ch, TARGET_PAGE_SIZE);
|
||||||
}
|
}
|
||||||
if (ch) {
|
if (ch) {
|
||||||
all_zero = false;
|
tmp_page->all_zero = false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RAM_SAVE_FLAG_PAGE:
|
case RAM_SAVE_FLAG_PAGE:
|
||||||
all_zero = false;
|
tmp_page->all_zero = false;
|
||||||
if (!matches_target_page_size) {
|
if (!matches_target_page_size) {
|
||||||
/* For huge pages, we always use temporary buffer */
|
/* For huge pages, we always use temporary buffer */
|
||||||
qemu_get_buffer(f, page_buffer, TARGET_PAGE_SIZE);
|
qemu_get_buffer(f, page_buffer, TARGET_PAGE_SIZE);
|
||||||
@ -3759,7 +3766,7 @@ static int ram_load_postcopy(QEMUFile *f)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case RAM_SAVE_FLAG_COMPRESS_PAGE:
|
case RAM_SAVE_FLAG_COMPRESS_PAGE:
|
||||||
all_zero = false;
|
tmp_page->all_zero = false;
|
||||||
len = qemu_get_be32(f);
|
len = qemu_get_be32(f);
|
||||||
if (len < 0 || len > compressBound(TARGET_PAGE_SIZE)) {
|
if (len < 0 || len > compressBound(TARGET_PAGE_SIZE)) {
|
||||||
error_report("Invalid compressed data length: %d", len);
|
error_report("Invalid compressed data length: %d", len);
|
||||||
@ -3791,16 +3798,14 @@ static int ram_load_postcopy(QEMUFile *f)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!ret && place_needed) {
|
if (!ret && place_needed) {
|
||||||
if (all_zero) {
|
if (tmp_page->all_zero) {
|
||||||
ret = postcopy_place_page_zero(mis, host_page, block);
|
ret = postcopy_place_page_zero(mis, tmp_page->host_addr, block);
|
||||||
} else {
|
} else {
|
||||||
ret = postcopy_place_page(mis, host_page, place_source,
|
ret = postcopy_place_page(mis, tmp_page->host_addr,
|
||||||
block);
|
place_source, block);
|
||||||
}
|
}
|
||||||
place_needed = false;
|
place_needed = false;
|
||||||
target_pages = 0;
|
postcopy_temp_page_reset(tmp_page);
|
||||||
/* Assume we have a zero page until we detect something different */
|
|
||||||
all_zero = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3880,6 +3885,7 @@ void colo_flush_ram_cache(void)
|
|||||||
*/
|
*/
|
||||||
static int ram_load_precopy(QEMUFile *f)
|
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;
|
int flags = 0, ret = 0, invalid_flags = 0, len = 0, i = 0;
|
||||||
/* ADVISE is earlier, it shows the source has the postcopy capability on */
|
/* ADVISE is earlier, it shows the source has the postcopy capability on */
|
||||||
bool postcopy_advised = postcopy_is_advised();
|
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 |
|
if (flags & (RAM_SAVE_FLAG_ZERO | RAM_SAVE_FLAG_PAGE |
|
||||||
RAM_SAVE_FLAG_COMPRESS_PAGE | RAM_SAVE_FLAG_XBZRLE)) {
|
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);
|
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";
|
char ip[40] = "unknown";
|
||||||
struct rdma_addrinfo *res, *e;
|
struct rdma_addrinfo *res, *e;
|
||||||
char port_str[16];
|
char port_str[16];
|
||||||
|
int reuse = 1;
|
||||||
|
|
||||||
for (idx = 0; idx < RDMA_WRID_MAX; idx++) {
|
for (idx = 0; idx < RDMA_WRID_MAX; idx++) {
|
||||||
rdma->wr_data[idx].control_len = 0;
|
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;
|
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) {
|
for (e = res; e != NULL; e = e->ai_next) {
|
||||||
inet_ntop(e->ai_family,
|
inet_ntop(e->ai_family,
|
||||||
&((struct sockaddr_in *) e->ai_dst_addr)->sin_addr, ip, sizeof ip);
|
&((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,
|
migrate_set_state(&mis->state, MIGRATION_STATUS_ACTIVE,
|
||||||
MIGRATION_STATUS_POSTCOPY_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();
|
trace_postcopy_ram_listen_thread_start();
|
||||||
|
|
||||||
rcu_register_thread();
|
rcu_register_thread();
|
||||||
@ -1948,9 +1948,10 @@ static void *postcopy_ram_listen_thread(void *opaque)
|
|||||||
static int loadvm_postcopy_handle_listen(MigrationIncomingState *mis)
|
static int loadvm_postcopy_handle_listen(MigrationIncomingState *mis)
|
||||||
{
|
{
|
||||||
PostcopyState ps = postcopy_state_set(POSTCOPY_INCOMING_LISTENING);
|
PostcopyState ps = postcopy_state_set(POSTCOPY_INCOMING_LISTENING);
|
||||||
trace_loadvm_postcopy_handle_listen();
|
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
|
|
||||||
|
trace_loadvm_postcopy_handle_listen("enter");
|
||||||
|
|
||||||
if (ps != POSTCOPY_INCOMING_ADVISE && ps != POSTCOPY_INCOMING_DISCARD) {
|
if (ps != POSTCOPY_INCOMING_ADVISE && ps != POSTCOPY_INCOMING_DISCARD) {
|
||||||
error_report("CMD_POSTCOPY_LISTEN in wrong postcopy state (%d)", ps);
|
error_report("CMD_POSTCOPY_LISTEN in wrong postcopy state (%d)", ps);
|
||||||
return -1;
|
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
|
* 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
|
* 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)) {
|
if (postcopy_notify(POSTCOPY_NOTIFY_INBOUND_LISTEN, &local_err)) {
|
||||||
error_report_err(local_err);
|
error_report_err(local_err);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
mis->have_listen_thread = true;
|
mis->have_listen_thread = true;
|
||||||
/* Start up the listening thread and wait for it to signal ready */
|
postcopy_thread_create(mis, &mis->listen_thread, "postcopy/listen",
|
||||||
qemu_sem_init(&mis->listen_thread_sem, 0);
|
postcopy_ram_listen_thread, QEMU_THREAD_DETACHED);
|
||||||
qemu_thread_create(&mis->listen_thread, "postcopy/listen",
|
trace_loadvm_postcopy_handle_listen("return");
|
||||||
postcopy_ram_listen_thread, NULL,
|
|
||||||
QEMU_THREAD_DETACHED);
|
|
||||||
qemu_sem_wait(&mis->listen_thread_sem);
|
|
||||||
qemu_sem_destroy(&mis->listen_thread_sem);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -1999,13 +2000,19 @@ static void loadvm_postcopy_handle_run_bh(void *opaque)
|
|||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
MigrationIncomingState *mis = opaque;
|
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
|
/* TODO we should move all of this lot into postcopy_ram.c or a shared code
|
||||||
* in migration.c
|
* in migration.c
|
||||||
*/
|
*/
|
||||||
cpu_synchronize_all_post_init();
|
cpu_synchronize_all_post_init();
|
||||||
|
|
||||||
|
trace_loadvm_postcopy_handle_run_bh("after cpu sync");
|
||||||
|
|
||||||
qemu_announce_self(&mis->announce_timer, migrate_announce_params());
|
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.
|
/* Make sure all file formats flush their mutable metadata.
|
||||||
* If we get an error here, just don't restart the VM yet. */
|
* If we get an error here, just don't restart the VM yet. */
|
||||||
bdrv_invalidate_cache_all(&local_err);
|
bdrv_invalidate_cache_all(&local_err);
|
||||||
@ -2015,9 +2022,7 @@ static void loadvm_postcopy_handle_run_bh(void *opaque)
|
|||||||
autostart = false;
|
autostart = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
trace_loadvm_postcopy_handle_run_cpu_sync();
|
trace_loadvm_postcopy_handle_run_bh("after invalidate cache");
|
||||||
|
|
||||||
trace_loadvm_postcopy_handle_run_vmstart();
|
|
||||||
|
|
||||||
dirty_bitmap_mig_before_vm_start();
|
dirty_bitmap_mig_before_vm_start();
|
||||||
|
|
||||||
@ -2030,6 +2035,8 @@ static void loadvm_postcopy_handle_run_bh(void *opaque)
|
|||||||
}
|
}
|
||||||
|
|
||||||
qemu_bh_delete(mis->bh);
|
qemu_bh_delete(mis->bh);
|
||||||
|
|
||||||
|
trace_loadvm_postcopy_handle_run_bh("return");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* After all discards we can start running and asking for pages */
|
/* 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);
|
return qemu_file_get_error(f);
|
||||||
}
|
}
|
||||||
|
|
||||||
trace_loadvm_process_command(cmd, len);
|
|
||||||
if (cmd >= MIG_CMD_MAX || cmd == MIG_CMD_INVALID) {
|
if (cmd >= MIG_CMD_MAX || cmd == MIG_CMD_INVALID) {
|
||||||
error_report("MIG_CMD 0x%x unknown (len 0x%x)", cmd, len);
|
error_report("MIG_CMD 0x%x unknown (len 0x%x)", cmd, len);
|
||||||
return -EINVAL;
|
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) {
|
if (mig_cmd_args[cmd].len != -1 && mig_cmd_args[cmd].len != len) {
|
||||||
error_report("%s received with bad length - expecting %zu, got %d",
|
error_report("%s received with bad length - expecting %zu, got %d",
|
||||||
mig_cmd_args[cmd].name,
|
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. */
|
/* Return true if we should continue the migration, or false. */
|
||||||
static bool postcopy_pause_incoming(MigrationIncomingState *mis)
|
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();
|
trace_postcopy_pause_incoming();
|
||||||
|
|
||||||
assert(migrate_postcopy_ram());
|
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_cmd_packaged_received(int ret) "%d"
|
||||||
loadvm_handle_recv_bitmap(char *s) "%s"
|
loadvm_handle_recv_bitmap(char *s) "%s"
|
||||||
loadvm_postcopy_handle_advise(void) ""
|
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(void) ""
|
||||||
loadvm_postcopy_handle_run_cpu_sync(void) ""
|
loadvm_postcopy_handle_run_bh(const char *str) "%s"
|
||||||
loadvm_postcopy_handle_run_vmstart(void) ""
|
|
||||||
loadvm_postcopy_handle_resume(void) ""
|
loadvm_postcopy_handle_resume(void) ""
|
||||||
loadvm_postcopy_ram_handle_discard(void) ""
|
loadvm_postcopy_ram_handle_discard(void) ""
|
||||||
loadvm_postcopy_ram_handle_discard_end(void) ""
|
loadvm_postcopy_ram_handle_discard_end(void) ""
|
||||||
loadvm_postcopy_ram_handle_discard_header(const char *ramid, uint16_t len) "%s: %ud"
|
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"
|
loadvm_process_command_ping(uint32_t val) "0x%x"
|
||||||
postcopy_ram_listen_thread_exit(void) ""
|
postcopy_ram_listen_thread_exit(void) ""
|
||||||
postcopy_ram_listen_thread_start(void) ""
|
postcopy_ram_listen_thread_start(void) ""
|
||||||
|
@ -90,12 +90,6 @@ int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd,
|
|||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
if (version_id < vmsd->minimum_version_id) {
|
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 "
|
error_report("%s: incoming version_id %d is too old "
|
||||||
"for local minimum version_id %d",
|
"for local minimum version_id %d",
|
||||||
vmsd->name, version_id, vmsd->minimum_version_id);
|
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 *protocol = qdict_get_str(qdict, "protocol");
|
||||||
const char *password = qdict_get_str(qdict, "password");
|
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");
|
const char *connected = qdict_get_try_str(qdict, "connected");
|
||||||
Error *err = NULL;
|
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);
|
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 *protocol = qdict_get_str(qdict, "protocol");
|
||||||
const char *whenstr = qdict_get_str(qdict, "time");
|
const char *whenstr = qdict_get_str(qdict, "time");
|
||||||
|
const char *display = qdict_get_try_str(qdict, "display");
|
||||||
Error *err = NULL;
|
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);
|
hmp_handle_error(mon, err);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -981,6 +981,7 @@ static QDict *monitor_parse_arguments(Monitor *mon,
|
|||||||
{
|
{
|
||||||
const char *tmp = p;
|
const char *tmp = p;
|
||||||
int skip_key = 0;
|
int skip_key = 0;
|
||||||
|
int ret;
|
||||||
/* option */
|
/* option */
|
||||||
|
|
||||||
c = *typestr++;
|
c = *typestr++;
|
||||||
@ -1003,11 +1004,27 @@ static QDict *monitor_parse_arguments(Monitor *mon,
|
|||||||
}
|
}
|
||||||
if (skip_key) {
|
if (skip_key) {
|
||||||
p = tmp;
|
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 {
|
} else {
|
||||||
/* has option */
|
/* has boolean option */
|
||||||
p++;
|
p++;
|
||||||
qdict_put_bool(qdict, key, true);
|
qdict_put_bool(qdict, key, true);
|
||||||
}
|
}
|
||||||
|
} else if (*typestr == 's') {
|
||||||
|
typestr++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -63,7 +63,8 @@
|
|||||||
* '.' other form of optional type (for 'i' and 'l')
|
* '.' other form of optional type (for 'i' and 'l')
|
||||||
* 'b' boolean
|
* 'b' boolean
|
||||||
* user mode accepts "on" or "off"
|
* 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);
|
qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER, errp);
|
||||||
}
|
}
|
||||||
|
|
||||||
void qmp_set_password(const char *protocol, const char *password,
|
void qmp_set_password(SetPasswordOptions *opts, Error **errp)
|
||||||
bool has_connected, const char *connected, Error **errp)
|
|
||||||
{
|
{
|
||||||
int disconnect_if_connected = 0;
|
|
||||||
int fail_if_connected = 0;
|
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
if (has_connected) {
|
if (opts->protocol == DISPLAY_PROTOCOL_SPICE) {
|
||||||
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 (!qemu_using_spice(errp)) {
|
if (!qemu_using_spice(errp)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
rc = qemu_spice.set_passwd(password, fail_if_connected,
|
rc = qemu_spice.set_passwd(opts->password,
|
||||||
disconnect_if_connected);
|
opts->connected == SET_PASSWORD_ACTION_FAIL,
|
||||||
} else if (strcmp(protocol, "vnc") == 0) {
|
opts->connected == SET_PASSWORD_ACTION_DISCONNECT);
|
||||||
if (fail_if_connected || disconnect_if_connected) {
|
} else {
|
||||||
|
assert(opts->protocol == DISPLAY_PROTOCOL_VNC);
|
||||||
|
if (opts->connected != SET_PASSWORD_ACTION_KEEP) {
|
||||||
/* vnc supports "connected=keep" only */
|
/* vnc supports "connected=keep" only */
|
||||||
error_setg(errp, QERR_INVALID_PARAMETER, "connected");
|
error_setg(errp, QERR_INVALID_PARAMETER, "connected");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
/* Note that setting an empty password will not disable login through
|
/* Note that setting an empty password will not disable login through
|
||||||
* this interface. */
|
* this interface. */
|
||||||
rc = vnc_display_password(NULL, password);
|
rc = vnc_display_password(opts->u.vnc.display, opts->password);
|
||||||
} else {
|
|
||||||
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "protocol",
|
|
||||||
"'vnc' or 'spice'");
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rc != 0) {
|
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,
|
void qmp_expire_password(ExpirePasswordOptions *opts, Error **errp)
|
||||||
Error **errp)
|
|
||||||
{
|
{
|
||||||
time_t when;
|
time_t when;
|
||||||
int rc;
|
int rc;
|
||||||
|
const char *whenstr = opts->time;
|
||||||
|
|
||||||
if (strcmp(whenstr, "now") == 0) {
|
if (strcmp(whenstr, "now") == 0) {
|
||||||
when = 0;
|
when = 0;
|
||||||
@ -230,17 +212,14 @@ void qmp_expire_password(const char *protocol, const char *whenstr,
|
|||||||
when = strtoull(whenstr, NULL, 10);
|
when = strtoull(whenstr, NULL, 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strcmp(protocol, "spice") == 0) {
|
if (opts->protocol == DISPLAY_PROTOCOL_SPICE) {
|
||||||
if (!qemu_using_spice(errp)) {
|
if (!qemu_using_spice(errp)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
rc = qemu_spice.set_pw_expire(when);
|
rc = qemu_spice.set_pw_expire(when);
|
||||||
} else if (strcmp(protocol, "vnc") == 0) {
|
|
||||||
rc = vnc_display_pw_expire(NULL, when);
|
|
||||||
} else {
|
} else {
|
||||||
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "protocol",
|
assert(opts->protocol == DISPLAY_PROTOCOL_VNC);
|
||||||
"'vnc' or 'spice'");
|
rc = vnc_display_pw_expire(opts->u.vnc.display, when);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rc != 0) {
|
if (rc != 0) {
|
||||||
|
120
qapi/ui.json
120
qapi/ui.json
@ -10,20 +10,75 @@
|
|||||||
{ 'include': 'sockets.json' }
|
{ '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
|
# @protocol: - 'vnc' to modify the VNC server password
|
||||||
# - 'spice' to modify the Spice server password
|
# - 'spice' to modify the Spice server password
|
||||||
#
|
#
|
||||||
# @password: the new password
|
# @password: the new password
|
||||||
#
|
#
|
||||||
# @connected: how to handle existing clients when changing the
|
# @connected: How to handle existing clients when changing the
|
||||||
# password. If nothing is specified, defaults to 'keep'
|
# password. If nothing is specified, defaults to 'keep'.
|
||||||
# 'fail' to fail the command if clients are connected
|
# For VNC, only 'keep' is currently implemented.
|
||||||
# 'disconnect' to disconnect existing clients
|
#
|
||||||
# 'keep' to maintain existing clients
|
# 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
|
# Returns: - Nothing on success
|
||||||
# - If Spice is not enabled, DeviceNotFound
|
# - If Spice is not enabled, DeviceNotFound
|
||||||
@ -37,15 +92,15 @@
|
|||||||
# <- { "return": {} }
|
# <- { "return": {} }
|
||||||
#
|
#
|
||||||
##
|
##
|
||||||
{ 'command': 'set_password',
|
{ 'command': 'set_password', 'boxed': true, 'data': 'SetPasswordOptions' }
|
||||||
'data': {'protocol': 'str', 'password': 'str', '*connected': 'str'} }
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# @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.
|
# @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 number of seconds from now (integer)
|
||||||
# - 'INT' where INT is the absolute time in seconds
|
# - '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
|
# 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
|
# coordinate server time with client time. It is not recommended to
|
||||||
# use the absolute time version of the @time parameter unless you're
|
# use the absolute time version of the @time parameter unless you're
|
||||||
# sure you are on the same machine as the QEMU instance.
|
# 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:
|
# Example:
|
||||||
#
|
#
|
||||||
# -> { "execute": "expire_password", "arguments": { "protocol": "vnc",
|
# -> { "execute": "expire_password", "arguments": { "protocol": "vnc",
|
||||||
@ -71,7 +155,7 @@
|
|||||||
# <- { "return": {} }
|
# <- { "return": {} }
|
||||||
#
|
#
|
||||||
##
|
##
|
||||||
{ 'command': 'expire_password', 'data': {'protocol': 'str', 'time': 'str'} }
|
{ 'command': 'expire_password', 'boxed': true, 'data': 'ExpirePasswordOptions' }
|
||||||
|
|
||||||
##
|
##
|
||||||
# @screendump:
|
# @screendump:
|
||||||
|
@ -495,7 +495,7 @@ static void migrate_start_destroy(MigrateStart *args)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int test_migrate_start(QTestState **from, QTestState **to,
|
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_source = NULL;
|
||||||
g_autofree gchar *arch_target = 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;
|
g_autofree char *shmem_path = NULL;
|
||||||
const char *arch = qtest_get_arch();
|
const char *arch = qtest_get_arch();
|
||||||
const char *machine_opts = NULL;
|
const char *machine_opts = NULL;
|
||||||
|
MigrateStart *args = *pargs;
|
||||||
const char *memory_size;
|
const char *memory_size;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
@ -621,6 +622,8 @@ static int test_migrate_start(QTestState **from, QTestState **to,
|
|||||||
|
|
||||||
out:
|
out:
|
||||||
migrate_start_destroy(args);
|
migrate_start_destroy(args);
|
||||||
|
/* This tells the caller that this structure is gone */
|
||||||
|
*pargs = NULL;
|
||||||
return ret;
|
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);
|
g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs);
|
||||||
QTestState *from, *to;
|
QTestState *from, *to;
|
||||||
|
|
||||||
if (test_migrate_start(&from, &to, uri, args)) {
|
if (test_migrate_start(&from, &to, uri, &args)) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -788,7 +791,7 @@ static void test_baddest(void)
|
|||||||
|
|
||||||
args->hide_stderr = true;
|
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;
|
return;
|
||||||
}
|
}
|
||||||
migrate_qmp(from, "tcp:127.0.0.1:0", "{}");
|
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;
|
args->use_dirty_ring = dirty_ring;
|
||||||
|
|
||||||
if (test_migrate_start(&from, &to, uri, args)) {
|
if (test_migrate_start(&from, &to, uri, &args)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -892,7 +895,7 @@ static void test_xbzrle(const char *uri)
|
|||||||
MigrateStart *args = migrate_start_new();
|
MigrateStart *args = migrate_start_new();
|
||||||
QTestState *from, *to;
|
QTestState *from, *to;
|
||||||
|
|
||||||
if (test_migrate_start(&from, &to, uri, args)) {
|
if (test_migrate_start(&from, &to, uri, &args)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -946,7 +949,7 @@ static void test_precopy_tcp(void)
|
|||||||
g_autofree char *uri = NULL;
|
g_autofree char *uri = NULL;
|
||||||
QTestState *from, *to;
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -991,7 +994,7 @@ static void test_migrate_fd_proto(void)
|
|||||||
QDict *rsp;
|
QDict *rsp;
|
||||||
const char *error_desc;
|
const char *error_desc;
|
||||||
|
|
||||||
if (test_migrate_start(&from, &to, "defer", args)) {
|
if (test_migrate_start(&from, &to, "defer", &args)) {
|
||||||
return;
|
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);
|
g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs);
|
||||||
QTestState *from, *to;
|
QTestState *from, *to;
|
||||||
|
|
||||||
if (test_migrate_start(&from, &to, uri, args)) {
|
if (test_migrate_start(&from, &to, uri, &args)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1163,7 +1166,7 @@ static void test_migrate_auto_converge(void)
|
|||||||
*/
|
*/
|
||||||
const int64_t expected_threshold = max_bandwidth * downtime_limit / 1000;
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1232,7 +1235,7 @@ static void test_multifd_tcp(const char *method)
|
|||||||
QDict *rsp;
|
QDict *rsp;
|
||||||
g_autofree char *uri = NULL;
|
g_autofree char *uri = NULL;
|
||||||
|
|
||||||
if (test_migrate_start(&from, &to, "defer", args)) {
|
if (test_migrate_start(&from, &to, "defer", &args)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1318,7 +1321,7 @@ static void test_multifd_tcp_cancel(void)
|
|||||||
|
|
||||||
args->hide_stderr = true;
|
args->hide_stderr = true;
|
||||||
|
|
||||||
if (test_migrate_start(&from, &to, "defer", args)) {
|
if (test_migrate_start(&from, &to, "defer", &args)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1357,7 +1360,7 @@ static void test_multifd_tcp_cancel(void)
|
|||||||
args = migrate_start_new();
|
args = migrate_start_new();
|
||||||
args->only_target = true;
|
args->only_target = true;
|
||||||
|
|
||||||
if (test_migrate_start(&from, &to2, "defer", args)) {
|
if (test_migrate_start(&from, &to2, "defer", &args)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1039,7 +1039,7 @@ static int do_statx(struct lo_data *lo, int dirfd, const char *pathname,
|
|||||||
{
|
{
|
||||||
int res;
|
int res;
|
||||||
|
|
||||||
#if defined(CONFIG_STATX) && defined(STATX_MNT_ID)
|
#if defined(CONFIG_STATX) && defined(CONFIG_STATX_MNT_ID)
|
||||||
if (lo->use_statx) {
|
if (lo->use_statx) {
|
||||||
struct statx statxbuf;
|
struct statx statxbuf;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user