mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
Merge tag 'migration-20250520-pull-request' of https://gitlab.com/peterx/qemu into staging
Migration pull - Peter's vmstate static checker entry for recent hpet change - Zhijian's rdma test patch to properly skip tests when locked_vm too low, and ipv6 test - Fabiano's few CI changes - Prasad's last patches to enable postcopy on precopy-multifd - Maciej's fix to disable multifd zerocopy for device states - Peter's HMP change to "info migrate", plus a small fix on cap set # -----BEGIN PGP SIGNATURE----- # # iIgEABYKADAWIQS5GE3CDMRX2s990ak7X8zN86vXBgUCaCy2xxIccGV0ZXJ4QHJl # ZGhhdC5jb20ACgkQO1/MzfOr1waNZgD9HRflpY6sqcXt9wPKYgJFAyPN9Jg05kGO # jeRIhD9cvyIBAN5AjbE0gaN8f+BLR5vcNk0Q5uOaHHIFZxL7tJl8ob4L # =WTpL # -----END PGP SIGNATURE----- # gpg: Signature made Tue 20 May 2025 13:07:19 EDT # gpg: using EDDSA key B9184DC20CC457DACF7DD1A93B5FCCCDF3ABD706 # gpg: issuer "peterx@redhat.com" # gpg: Good signature from "Peter Xu <xzpeter@gmail.com>" [full] # gpg: aka "Peter Xu <peterx@redhat.com>" [full] # Primary key fingerprint: B918 4DC2 0CC4 57DA CF7D D1A9 3B5F CCCD F3AB D706 * tag 'migration-20250520-pull-request' of https://gitlab.com/peterx/qemu: migration/hmp: Add "info migrate -a", reorg the dump migration: Allow caps to be set when preempt or multifd cap enabled migration/multifd: Don't send device state packets with zerocopy flag tests/qtest/migration: add postcopy tests with multifd migration: enable multifd and postcopy together migration: write zero pages when postcopy enabled ci: Reduce the size of artifacts for build-previous-qemu ci: Fix build-previous-qemu when the version tag is absent ci: Re-enable python subtests in qtest migration suite qtest/migration/rdma: Add test for rdma migration with ipv6 qtest/migration/rdma: Enforce RLIMIT_MEMLOCK >= 128MB requirement scripts/vmstate-static-checker.py: Add new hpet entry for num_timers Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
This commit is contained in:
+13
-14
@@ -188,12 +188,11 @@ build-previous-qemu:
|
||||
when: on_success
|
||||
expire_in: 2 days
|
||||
paths:
|
||||
- build-previous
|
||||
exclude:
|
||||
- build-previous/**/*.p
|
||||
- build-previous/**/*.a.p
|
||||
- build-previous/**/*.c.o
|
||||
- build-previous/**/*.c.o.d
|
||||
- build-previous/qemu-bundle
|
||||
- build-previous/qemu-system-aarch64
|
||||
- build-previous/qemu-system-x86_64
|
||||
- build-previous/tests/qtest/migration-test
|
||||
- build-previous/scripts
|
||||
needs:
|
||||
job: amd64-opensuse-leap-container
|
||||
variables:
|
||||
@@ -203,6 +202,11 @@ build-previous-qemu:
|
||||
GIT_FETCH_EXTRA_FLAGS: --prune --quiet
|
||||
before_script:
|
||||
- source scripts/ci/gitlab-ci-section
|
||||
# Skip if this series contains the release bump commit. During the
|
||||
# release process there might be a window of commits when the
|
||||
# version tag is not yet present in the remote and git fetch would
|
||||
# fail.
|
||||
- if grep -q "\.0$" VERSION; then exit 0; fi
|
||||
- export QEMU_PREV_VERSION="$(sed 's/\([0-9.]*\)\.[0-9]*/v\1.0/' VERSION)"
|
||||
- git remote add upstream https://gitlab.com/qemu-project/qemu
|
||||
- git fetch upstream refs/tags/$QEMU_PREV_VERSION:refs/tags/$QEMU_PREV_VERSION
|
||||
@@ -223,18 +227,13 @@ build-previous-qemu:
|
||||
IMAGE: opensuse-leap
|
||||
MAKE_CHECK_ARGS: check-build
|
||||
script:
|
||||
# Skip for round release numbers, this job is only relevant for
|
||||
# testing a development tree.
|
||||
- if grep -q "\.0$" VERSION; then exit 0; fi
|
||||
# Use the migration-tests from the older QEMU tree. This avoids
|
||||
# testing an old QEMU against new features/tests that it is not
|
||||
# compatible with.
|
||||
- cd build-previous
|
||||
# Don't allow python-based tests to run. The
|
||||
# vmstate-checker-script test has a race that causes it to fail
|
||||
# sometimes. It cannot be fixed it because this job runs the test
|
||||
# from the old QEMU version. The test will be removed on master,
|
||||
# but this job will only see the change in the next release.
|
||||
#
|
||||
# TODO: remove this line after 9.2 release
|
||||
- unset PYTHON
|
||||
# old to new
|
||||
- QTEST_QEMU_BINARY_SRC=./qemu-system-${TARGET}
|
||||
QTEST_QEMU_BINARY=../build/qemu-system-${TARGET} ./tests/qtest/migration-test
|
||||
|
||||
@@ -475,9 +475,9 @@ ERST
|
||||
|
||||
{
|
||||
.name = "migrate",
|
||||
.args_type = "",
|
||||
.params = "",
|
||||
.help = "show migration status",
|
||||
.args_type = "all:-a",
|
||||
.params = "[-a]",
|
||||
.help = "show migration status (-a: all, dump all status)",
|
||||
.cmd = hmp_info_migrate,
|
||||
},
|
||||
|
||||
|
||||
@@ -37,29 +37,28 @@ static void migration_global_dump(Monitor *mon)
|
||||
{
|
||||
MigrationState *ms = migrate_get_current();
|
||||
|
||||
monitor_printf(mon, "globals:\n");
|
||||
monitor_printf(mon, "store-global-state: %s\n",
|
||||
monitor_printf(mon, "Globals:\n");
|
||||
monitor_printf(mon, " store-global-state: %s\n",
|
||||
ms->store_global_state ? "on" : "off");
|
||||
monitor_printf(mon, "only-migratable: %s\n",
|
||||
monitor_printf(mon, " only-migratable: %s\n",
|
||||
only_migratable ? "on" : "off");
|
||||
monitor_printf(mon, "send-configuration: %s\n",
|
||||
monitor_printf(mon, " send-configuration: %s\n",
|
||||
ms->send_configuration ? "on" : "off");
|
||||
monitor_printf(mon, "send-section-footer: %s\n",
|
||||
monitor_printf(mon, " send-section-footer: %s\n",
|
||||
ms->send_section_footer ? "on" : "off");
|
||||
monitor_printf(mon, "send-switchover-start: %s\n",
|
||||
monitor_printf(mon, " send-switchover-start: %s\n",
|
||||
ms->send_switchover_start ? "on" : "off");
|
||||
monitor_printf(mon, "clear-bitmap-shift: %u\n",
|
||||
monitor_printf(mon, " clear-bitmap-shift: %u\n",
|
||||
ms->clear_bitmap_shift);
|
||||
}
|
||||
|
||||
void hmp_info_migrate(Monitor *mon, const QDict *qdict)
|
||||
{
|
||||
bool show_all = qdict_get_try_bool(qdict, "all", false);
|
||||
MigrationInfo *info;
|
||||
|
||||
info = qmp_query_migrate(NULL);
|
||||
|
||||
migration_global_dump(mon);
|
||||
|
||||
if (info->blocked_reasons) {
|
||||
strList *reasons = info->blocked_reasons;
|
||||
monitor_printf(mon, "Outgoing migration blocked:\n");
|
||||
@@ -70,7 +69,7 @@ void hmp_info_migrate(Monitor *mon, const QDict *qdict)
|
||||
}
|
||||
|
||||
if (info->has_status) {
|
||||
monitor_printf(mon, "Migration status: %s",
|
||||
monitor_printf(mon, "Status: %s",
|
||||
MigrationStatus_str(info->status));
|
||||
if (info->status == MIGRATION_STATUS_FAILED && info->error_desc) {
|
||||
monitor_printf(mon, " (%s)\n", info->error_desc);
|
||||
@@ -78,107 +77,130 @@ void hmp_info_migrate(Monitor *mon, const QDict *qdict)
|
||||
monitor_printf(mon, "\n");
|
||||
}
|
||||
|
||||
monitor_printf(mon, "total time: %" PRIu64 " ms\n",
|
||||
info->total_time);
|
||||
if (info->has_expected_downtime) {
|
||||
monitor_printf(mon, "expected downtime: %" PRIu64 " ms\n",
|
||||
info->expected_downtime);
|
||||
if (info->total_time) {
|
||||
monitor_printf(mon, "Time (ms): total=%" PRIu64,
|
||||
info->total_time);
|
||||
if (info->has_setup_time) {
|
||||
monitor_printf(mon, ", setup=%" PRIu64,
|
||||
info->setup_time);
|
||||
}
|
||||
if (info->has_expected_downtime) {
|
||||
monitor_printf(mon, ", exp_down=%" PRIu64,
|
||||
info->expected_downtime);
|
||||
}
|
||||
if (info->has_downtime) {
|
||||
monitor_printf(mon, ", down=%" PRIu64,
|
||||
info->downtime);
|
||||
}
|
||||
monitor_printf(mon, "\n");
|
||||
}
|
||||
if (info->has_downtime) {
|
||||
monitor_printf(mon, "downtime: %" PRIu64 " ms\n",
|
||||
info->downtime);
|
||||
}
|
||||
if (info->has_setup_time) {
|
||||
monitor_printf(mon, "setup: %" PRIu64 " ms\n",
|
||||
info->setup_time);
|
||||
}
|
||||
|
||||
if (info->has_socket_address) {
|
||||
SocketAddressList *addr;
|
||||
|
||||
monitor_printf(mon, "Sockets: [\n");
|
||||
|
||||
for (addr = info->socket_address; addr; addr = addr->next) {
|
||||
char *s = socket_uri(addr->value);
|
||||
monitor_printf(mon, "\t%s\n", s);
|
||||
g_free(s);
|
||||
}
|
||||
monitor_printf(mon, "]\n");
|
||||
}
|
||||
|
||||
if (info->ram) {
|
||||
monitor_printf(mon, "transferred ram: %" PRIu64 " kbytes\n",
|
||||
info->ram->transferred >> 10);
|
||||
monitor_printf(mon, "throughput: %0.2f mbps\n",
|
||||
monitor_printf(mon, "RAM info:\n");
|
||||
monitor_printf(mon, " Throughput (Mbps): %0.2f\n",
|
||||
info->ram->mbps);
|
||||
monitor_printf(mon, "remaining ram: %" PRIu64 " kbytes\n",
|
||||
info->ram->remaining >> 10);
|
||||
monitor_printf(mon, "total ram: %" PRIu64 " kbytes\n",
|
||||
monitor_printf(mon, " Sizes (KiB): pagesize=%" PRIu64
|
||||
", total=%" PRIu64 ",\n",
|
||||
info->ram->page_size >> 10,
|
||||
info->ram->total >> 10);
|
||||
monitor_printf(mon, "duplicate: %" PRIu64 " pages\n",
|
||||
info->ram->duplicate);
|
||||
monitor_printf(mon, "normal: %" PRIu64 " pages\n",
|
||||
info->ram->normal);
|
||||
monitor_printf(mon, "normal bytes: %" PRIu64 " kbytes\n",
|
||||
info->ram->normal_bytes >> 10);
|
||||
monitor_printf(mon, "dirty sync count: %" PRIu64 "\n",
|
||||
info->ram->dirty_sync_count);
|
||||
monitor_printf(mon, "page size: %" PRIu64 " kbytes\n",
|
||||
info->ram->page_size >> 10);
|
||||
monitor_printf(mon, "multifd bytes: %" PRIu64 " kbytes\n",
|
||||
info->ram->multifd_bytes >> 10);
|
||||
monitor_printf(mon, "pages-per-second: %" PRIu64 "\n",
|
||||
monitor_printf(mon, " transferred=%" PRIu64
|
||||
", remain=%" PRIu64 ",\n",
|
||||
info->ram->transferred >> 10,
|
||||
info->ram->remaining >> 10);
|
||||
monitor_printf(mon, " precopy=%" PRIu64
|
||||
", multifd=%" PRIu64
|
||||
", postcopy=%" PRIu64,
|
||||
info->ram->precopy_bytes >> 10,
|
||||
info->ram->multifd_bytes >> 10,
|
||||
info->ram->postcopy_bytes >> 10);
|
||||
|
||||
if (info->vfio) {
|
||||
monitor_printf(mon, ", vfio=%" PRIu64,
|
||||
info->vfio->transferred >> 10);
|
||||
}
|
||||
monitor_printf(mon, "\n");
|
||||
|
||||
monitor_printf(mon, " Pages: normal=%" PRIu64 ", zero=%" PRIu64
|
||||
", rate_per_sec=%" PRIu64 "\n",
|
||||
info->ram->normal,
|
||||
info->ram->duplicate,
|
||||
info->ram->pages_per_second);
|
||||
monitor_printf(mon, " Others: dirty_syncs=%" PRIu64,
|
||||
info->ram->dirty_sync_count);
|
||||
|
||||
if (info->ram->dirty_pages_rate) {
|
||||
monitor_printf(mon, "dirty pages rate: %" PRIu64 " pages\n",
|
||||
monitor_printf(mon, ", dirty_pages_rate=%" PRIu64,
|
||||
info->ram->dirty_pages_rate);
|
||||
}
|
||||
if (info->ram->postcopy_requests) {
|
||||
monitor_printf(mon, "postcopy request count: %" PRIu64 "\n",
|
||||
monitor_printf(mon, ", postcopy_req=%" PRIu64,
|
||||
info->ram->postcopy_requests);
|
||||
}
|
||||
if (info->ram->precopy_bytes) {
|
||||
monitor_printf(mon, "precopy ram: %" PRIu64 " kbytes\n",
|
||||
info->ram->precopy_bytes >> 10);
|
||||
}
|
||||
if (info->ram->downtime_bytes) {
|
||||
monitor_printf(mon, "downtime ram: %" PRIu64 " kbytes\n",
|
||||
info->ram->downtime_bytes >> 10);
|
||||
}
|
||||
if (info->ram->postcopy_bytes) {
|
||||
monitor_printf(mon, "postcopy ram: %" PRIu64 " kbytes\n",
|
||||
info->ram->postcopy_bytes >> 10);
|
||||
monitor_printf(mon, ", downtime_ram=%" PRIu64,
|
||||
info->ram->downtime_bytes);
|
||||
}
|
||||
if (info->ram->dirty_sync_missed_zero_copy) {
|
||||
monitor_printf(mon,
|
||||
"Zero-copy-send fallbacks happened: %" PRIu64 " times\n",
|
||||
monitor_printf(mon, ", zerocopy_fallbacks=%" PRIu64,
|
||||
info->ram->dirty_sync_missed_zero_copy);
|
||||
}
|
||||
monitor_printf(mon, "\n");
|
||||
}
|
||||
|
||||
if (!show_all) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
migration_global_dump(mon);
|
||||
|
||||
if (info->xbzrle_cache) {
|
||||
monitor_printf(mon, "cache size: %" PRIu64 " bytes\n",
|
||||
info->xbzrle_cache->cache_size);
|
||||
monitor_printf(mon, "xbzrle transferred: %" PRIu64 " kbytes\n",
|
||||
info->xbzrle_cache->bytes >> 10);
|
||||
monitor_printf(mon, "xbzrle pages: %" PRIu64 " pages\n",
|
||||
info->xbzrle_cache->pages);
|
||||
monitor_printf(mon, "xbzrle cache miss: %" PRIu64 " pages\n",
|
||||
info->xbzrle_cache->cache_miss);
|
||||
monitor_printf(mon, "xbzrle cache miss rate: %0.2f\n",
|
||||
info->xbzrle_cache->cache_miss_rate);
|
||||
monitor_printf(mon, "xbzrle encoding rate: %0.2f\n",
|
||||
info->xbzrle_cache->encoding_rate);
|
||||
monitor_printf(mon, "xbzrle overflow: %" PRIu64 "\n",
|
||||
monitor_printf(mon, "XBZRLE: size=%" PRIu64
|
||||
", transferred=%" PRIu64
|
||||
", pages=%" PRIu64
|
||||
", miss=%" PRIu64 "\n"
|
||||
" miss_rate=%0.2f"
|
||||
", encode_rate=%0.2f"
|
||||
", overflow=%" PRIu64 "\n",
|
||||
info->xbzrle_cache->cache_size,
|
||||
info->xbzrle_cache->bytes,
|
||||
info->xbzrle_cache->pages,
|
||||
info->xbzrle_cache->cache_miss,
|
||||
info->xbzrle_cache->cache_miss_rate,
|
||||
info->xbzrle_cache->encoding_rate,
|
||||
info->xbzrle_cache->overflow);
|
||||
}
|
||||
|
||||
if (info->has_cpu_throttle_percentage) {
|
||||
monitor_printf(mon, "cpu throttle percentage: %" PRIu64 "\n",
|
||||
monitor_printf(mon, "CPU Throttle (%%): %" PRIu64 "\n",
|
||||
info->cpu_throttle_percentage);
|
||||
}
|
||||
|
||||
if (info->has_dirty_limit_throttle_time_per_round) {
|
||||
monitor_printf(mon, "dirty-limit throttle time: %" PRIu64 " us\n",
|
||||
monitor_printf(mon, "Dirty-limit Throttle (us): %" PRIu64 "\n",
|
||||
info->dirty_limit_throttle_time_per_round);
|
||||
}
|
||||
|
||||
if (info->has_dirty_limit_ring_full_time) {
|
||||
monitor_printf(mon, "dirty-limit ring full time: %" PRIu64 " us\n",
|
||||
monitor_printf(mon, "Dirty-limit Ring Full (us): %" PRIu64 "\n",
|
||||
info->dirty_limit_ring_full_time);
|
||||
}
|
||||
|
||||
if (info->has_postcopy_blocktime) {
|
||||
monitor_printf(mon, "postcopy blocktime: %u\n",
|
||||
monitor_printf(mon, "Postcopy Blocktime (ms): %" PRIu32 "\n",
|
||||
info->postcopy_blocktime);
|
||||
}
|
||||
|
||||
@@ -189,28 +211,12 @@ void hmp_info_migrate(Monitor *mon, const QDict *qdict)
|
||||
visit_type_uint32List(v, NULL, &info->postcopy_vcpu_blocktime,
|
||||
&error_abort);
|
||||
visit_complete(v, &str);
|
||||
monitor_printf(mon, "postcopy vcpu blocktime: %s\n", str);
|
||||
monitor_printf(mon, "Postcopy vCPU Blocktime: %s\n", str);
|
||||
g_free(str);
|
||||
visit_free(v);
|
||||
}
|
||||
if (info->has_socket_address) {
|
||||
SocketAddressList *addr;
|
||||
|
||||
monitor_printf(mon, "socket address: [\n");
|
||||
|
||||
for (addr = info->socket_address; addr; addr = addr->next) {
|
||||
char *s = socket_uri(addr->value);
|
||||
monitor_printf(mon, "\t%s\n", s);
|
||||
g_free(s);
|
||||
}
|
||||
monitor_printf(mon, "]\n");
|
||||
}
|
||||
|
||||
if (info->vfio) {
|
||||
monitor_printf(mon, "vfio device transferred: %" PRIu64 " kbytes\n",
|
||||
info->vfio->transferred >> 10);
|
||||
}
|
||||
|
||||
out:
|
||||
qapi_free_MigrationInfo(info);
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "migration-stats.h"
|
||||
#include "multifd.h"
|
||||
#include "options.h"
|
||||
#include "migration.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu/cutils.h"
|
||||
#include "qemu/error-report.h"
|
||||
@@ -398,7 +399,7 @@ int multifd_ram_flush_and_sync(QEMUFile *f)
|
||||
MultiFDSyncReq req;
|
||||
int ret;
|
||||
|
||||
if (!migrate_multifd()) {
|
||||
if (!migrate_multifd() || migration_in_postcopy()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -85,9 +85,27 @@ void multifd_recv_zero_page_process(MultiFDRecvParams *p)
|
||||
{
|
||||
for (int i = 0; i < p->zero_num; i++) {
|
||||
void *page = p->host + p->zero[i];
|
||||
if (ramblock_recv_bitmap_test_byte_offset(p->block, p->zero[i])) {
|
||||
bool received =
|
||||
ramblock_recv_bitmap_test_byte_offset(p->block, p->zero[i]);
|
||||
|
||||
/*
|
||||
* During multifd migration zero page is written to the memory
|
||||
* only if it is migrated more than once.
|
||||
*
|
||||
* It becomes a problem when both multifd & postcopy options are
|
||||
* enabled. If the zero page which was skipped during multifd phase,
|
||||
* is accessed during the postcopy phase of the migration, a page
|
||||
* fault occurs. But this page fault is not served because the
|
||||
* 'receivedmap' says the zero page is already received. Thus the
|
||||
* thread accessing that page may hang.
|
||||
*
|
||||
* When postcopy is enabled, always write the zero page as and when
|
||||
* it is migrated.
|
||||
*/
|
||||
if (migrate_postcopy_ram() || received) {
|
||||
memset(page, 0, multifd_ram_page_size());
|
||||
} else {
|
||||
}
|
||||
if (!received) {
|
||||
ramblock_recv_bitmap_set_offset(p->block, p->zero[i]);
|
||||
}
|
||||
}
|
||||
|
||||
+13
-1
@@ -690,6 +690,7 @@ static void *multifd_send_thread(void *opaque)
|
||||
if (qatomic_load_acquire(&p->pending_job)) {
|
||||
bool is_device_state = multifd_payload_device_state(p->data);
|
||||
size_t total_size;
|
||||
int write_flags_masked = 0;
|
||||
|
||||
p->flags = 0;
|
||||
p->iovs_num = 0;
|
||||
@@ -697,6 +698,9 @@ static void *multifd_send_thread(void *opaque)
|
||||
|
||||
if (is_device_state) {
|
||||
multifd_device_state_send_prepare(p);
|
||||
|
||||
/* Device state packets cannot be sent via zerocopy */
|
||||
write_flags_masked |= QIO_CHANNEL_WRITE_FLAG_ZERO_COPY;
|
||||
} else {
|
||||
ret = multifd_send_state->ops->send_prepare(p, &local_err);
|
||||
if (ret != 0) {
|
||||
@@ -718,7 +722,8 @@ static void *multifd_send_thread(void *opaque)
|
||||
&p->data->u.ram, &local_err);
|
||||
} else {
|
||||
ret = qio_channel_writev_full_all(p->c, p->iov, p->iovs_num,
|
||||
NULL, 0, p->write_flags,
|
||||
NULL, 0,
|
||||
p->write_flags & ~write_flags_masked,
|
||||
&local_err);
|
||||
}
|
||||
|
||||
@@ -1379,6 +1384,13 @@ static void *multifd_recv_thread(void *opaque)
|
||||
}
|
||||
|
||||
if (has_data) {
|
||||
/*
|
||||
* multifd thread should not be active and receive data
|
||||
* when migration is in the Postcopy phase. Two threads
|
||||
* writing the same memory area could easily corrupt
|
||||
* the guest state.
|
||||
*/
|
||||
assert(!migration_in_postcopy());
|
||||
if (is_device_state) {
|
||||
assert(use_packets);
|
||||
ret = multifd_device_state_recv(p, &local_err);
|
||||
|
||||
+2
-7
@@ -509,11 +509,6 @@ bool migrate_caps_check(bool *old_caps, bool *new_caps, Error **errp)
|
||||
error_setg(errp, "Postcopy is not compatible with ignore-shared");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (new_caps[MIGRATION_CAPABILITY_MULTIFD]) {
|
||||
error_setg(errp, "Postcopy is not yet compatible with multifd");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (new_caps[MIGRATION_CAPABILITY_BACKGROUND_SNAPSHOT]) {
|
||||
@@ -573,7 +568,7 @@ bool migrate_caps_check(bool *old_caps, bool *new_caps, Error **errp)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (migrate_incoming_started()) {
|
||||
if (!migrate_postcopy_preempt() && migrate_incoming_started()) {
|
||||
error_setg(errp,
|
||||
"Postcopy preempt must be set before incoming starts");
|
||||
return false;
|
||||
@@ -581,7 +576,7 @@ bool migrate_caps_check(bool *old_caps, bool *new_caps, Error **errp)
|
||||
}
|
||||
|
||||
if (new_caps[MIGRATION_CAPABILITY_MULTIFD]) {
|
||||
if (migrate_incoming_started()) {
|
||||
if (!migrate_multifd() && migrate_incoming_started()) {
|
||||
error_setg(errp, "Multifd must be set before incoming starts");
|
||||
return false;
|
||||
}
|
||||
|
||||
+2
-3
@@ -1993,9 +1993,8 @@ static int ram_save_target_page(RAMState *rs, PageSearchStatus *pss)
|
||||
}
|
||||
}
|
||||
|
||||
if (migrate_multifd()) {
|
||||
RAMBlock *block = pss->block;
|
||||
return ram_save_multifd_page(block, offset);
|
||||
if (migrate_multifd() && !migration_in_postcopy()) {
|
||||
return ram_save_multifd_page(pss->block, offset);
|
||||
}
|
||||
|
||||
return ram_save_page(rs, pss);
|
||||
|
||||
@@ -8,23 +8,44 @@ get_ipv4_addr()
|
||||
head -1 | tr -d '\n'
|
||||
}
|
||||
|
||||
get_ipv6_addr() {
|
||||
ipv6=$(ip -6 -o addr show dev "$1" |
|
||||
sed -n 's/.*[[:blank:]]inet6[[:blank:]]*\([^[:blank:]/]*\).*/\1/p' |
|
||||
head -1 | tr -d '\n')
|
||||
|
||||
[ $? -eq 0 ] || return
|
||||
|
||||
if [[ "$ipv6" =~ ^fe80: ]]; then
|
||||
echo -n "[$ipv6%$1]"
|
||||
else
|
||||
echo -n "[$ipv6]"
|
||||
fi
|
||||
}
|
||||
|
||||
# existing rdma interfaces
|
||||
rdma_interfaces()
|
||||
{
|
||||
rdma link show | sed -nE 's/^link .* netdev ([^ ]+).*$/\1 /p'
|
||||
rdma link show | sed -nE 's/^link .* netdev ([^ ]+).*$/\1 /p' |
|
||||
grep -Ev '^(lo|tun|tap)'
|
||||
}
|
||||
|
||||
# existing valid ipv4 interfaces
|
||||
ipv4_interfaces()
|
||||
{
|
||||
ip -o addr show | awk '/inet / {print $2}' | grep -v -w lo
|
||||
ip -o addr show | awk '/inet / {print $2}' | grep -Ev '^(lo|tun|tap)'
|
||||
}
|
||||
|
||||
ipv6_interfaces()
|
||||
{
|
||||
ip -o addr show | awk '/inet6 / {print $2}' | grep -Ev '^(lo|tun|tap)'
|
||||
}
|
||||
|
||||
rdma_rxe_detect()
|
||||
{
|
||||
family=$1
|
||||
for r in $(rdma_interfaces)
|
||||
do
|
||||
ipv4_interfaces | grep -qw $r && get_ipv4_addr $r && return
|
||||
"$family"_interfaces | grep -qw $r && get_"$family"_addr $r && return
|
||||
done
|
||||
|
||||
return 1
|
||||
@@ -32,16 +53,23 @@ rdma_rxe_detect()
|
||||
|
||||
rdma_rxe_setup()
|
||||
{
|
||||
for i in $(ipv4_interfaces)
|
||||
family=$1
|
||||
for i in $("$family"_interfaces)
|
||||
do
|
||||
rdma_interfaces | grep -qw $i && continue
|
||||
if rdma_interfaces | grep -qw $i; then
|
||||
echo "$family: Reuse the existing rdma/rxe ${i}_rxe" \
|
||||
"for $i with $(get_"$family"_addr $i)"
|
||||
return
|
||||
fi
|
||||
|
||||
rdma link add "${i}_rxe" type rxe netdev "$i" && {
|
||||
echo "Setup new rdma/rxe ${i}_rxe for $i with $(get_ipv4_addr $i)"
|
||||
echo "$family: Setup new rdma/rxe ${i}_rxe" \
|
||||
"for $i with $(get_"$family"_addr $i)"
|
||||
return
|
||||
}
|
||||
done
|
||||
|
||||
echo "Failed to setup any new rdma/rxe link" >&2
|
||||
echo "$family: Failed to setup any new rdma/rxe link" >&2
|
||||
return 1
|
||||
}
|
||||
|
||||
@@ -50,6 +78,12 @@ rdma_rxe_clean()
|
||||
modprobe -r rdma_rxe
|
||||
}
|
||||
|
||||
IP_FAMILY=${IP_FAMILY:-ipv4}
|
||||
if [ "$IP_FAMILY" != "ipv6" ] && [ "$IP_FAMILY" != "ipv4" ]; then
|
||||
echo "Unknown ip family '$IP_FAMILY', only ipv4 or ipv6 is supported." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
operation=${1:-detect}
|
||||
|
||||
command -v rdma >/dev/null || {
|
||||
@@ -62,9 +96,14 @@ if [ "$operation" == "setup" ] || [ "$operation" == "clean" ]; then
|
||||
echo "Root privilege is required to setup/clean a rdma/rxe link" >&2
|
||||
exit 1
|
||||
}
|
||||
rdma_rxe_"$operation"
|
||||
if [ "$operation" == "setup" ]; then
|
||||
rdma_rxe_setup ipv4
|
||||
rdma_rxe_setup ipv6
|
||||
else
|
||||
rdma_rxe_clean
|
||||
fi
|
||||
elif [ "$operation" == "detect" ]; then
|
||||
rdma_rxe_detect
|
||||
rdma_rxe_detect "$IP_FAMILY"
|
||||
else
|
||||
echo "Usage: $0 [setup | detect | clean]"
|
||||
fi
|
||||
|
||||
@@ -91,6 +91,7 @@ def check_fields_match(name, s_field, d_field):
|
||||
'mem_win_size', 'mig_mem_win_size',
|
||||
'io_win_addr', 'mig_io_win_addr',
|
||||
'io_win_size', 'mig_io_win_size'],
|
||||
'hpet': ['num_timers', 'num_timers_save'],
|
||||
}
|
||||
|
||||
if not name in changed_names:
|
||||
|
||||
@@ -42,6 +42,20 @@ static void test_multifd_tcp_zstd(void)
|
||||
};
|
||||
test_precopy_common(&args);
|
||||
}
|
||||
|
||||
static void test_multifd_postcopy_tcp_zstd(void)
|
||||
{
|
||||
MigrateCommon args = {
|
||||
.listen_uri = "defer",
|
||||
.start = {
|
||||
.caps[MIGRATION_CAPABILITY_MULTIFD] = true,
|
||||
.caps[MIGRATION_CAPABILITY_POSTCOPY_RAM] = true,
|
||||
},
|
||||
.start_hook = migrate_hook_start_precopy_tcp_multifd_zstd,
|
||||
};
|
||||
|
||||
test_precopy_common(&args);
|
||||
}
|
||||
#endif /* CONFIG_ZSTD */
|
||||
|
||||
#ifdef CONFIG_QATZIP
|
||||
@@ -184,6 +198,10 @@ void migration_test_add_compression(MigrationTestEnv *env)
|
||||
#ifdef CONFIG_ZSTD
|
||||
migration_test_add("/migration/multifd/tcp/plain/zstd",
|
||||
test_multifd_tcp_zstd);
|
||||
if (env->has_uffd) {
|
||||
migration_test_add("/migration/multifd+postcopy/tcp/plain/zstd",
|
||||
test_multifd_postcopy_tcp_zstd);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_QATZIP
|
||||
|
||||
@@ -94,6 +94,29 @@ static void migration_test_add_postcopy_smoke(MigrationTestEnv *env)
|
||||
}
|
||||
}
|
||||
|
||||
static void test_multifd_postcopy(void)
|
||||
{
|
||||
MigrateCommon args = {
|
||||
.start = {
|
||||
.caps[MIGRATION_CAPABILITY_MULTIFD] = true,
|
||||
},
|
||||
};
|
||||
|
||||
test_postcopy_common(&args);
|
||||
}
|
||||
|
||||
static void test_multifd_postcopy_preempt(void)
|
||||
{
|
||||
MigrateCommon args = {
|
||||
.start = {
|
||||
.caps[MIGRATION_CAPABILITY_MULTIFD] = true,
|
||||
.caps[MIGRATION_CAPABILITY_POSTCOPY_PREEMPT] = true,
|
||||
},
|
||||
};
|
||||
|
||||
test_postcopy_common(&args);
|
||||
}
|
||||
|
||||
void migration_test_add_postcopy(MigrationTestEnv *env)
|
||||
{
|
||||
migration_test_add_postcopy_smoke(env);
|
||||
@@ -114,6 +137,10 @@ void migration_test_add_postcopy(MigrationTestEnv *env)
|
||||
"/migration/postcopy/recovery/double-failures/reconnect",
|
||||
test_postcopy_recovery_fail_reconnect);
|
||||
|
||||
migration_test_add("/migration/multifd+postcopy/plain",
|
||||
test_multifd_postcopy);
|
||||
migration_test_add("/migration/multifd+postcopy/preempt/plain",
|
||||
test_multifd_postcopy_preempt);
|
||||
if (env->is_x86) {
|
||||
migration_test_add("/migration/postcopy/suspend",
|
||||
test_postcopy_suspend);
|
||||
|
||||
@@ -101,13 +101,43 @@ static void test_precopy_unix_dirty_ring(void)
|
||||
|
||||
#ifdef CONFIG_RDMA
|
||||
|
||||
#include <sys/resource.h>
|
||||
|
||||
/*
|
||||
* During migration over RDMA, it will try to pin portions of guest memory,
|
||||
* typically exceeding 100MB in this test, while the remainder will be
|
||||
* transmitted as compressed zero pages.
|
||||
*
|
||||
* REQUIRED_MEMLOCK_SZ indicates the minimal mlock size in the current context.
|
||||
*/
|
||||
#define REQUIRED_MEMLOCK_SZ (128 << 20) /* 128MB */
|
||||
|
||||
/* check 'ulimit -l' */
|
||||
static bool mlock_check(void)
|
||||
{
|
||||
uid_t uid;
|
||||
struct rlimit rlim;
|
||||
|
||||
uid = getuid();
|
||||
if (uid == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (getrlimit(RLIMIT_MEMLOCK, &rlim) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return rlim.rlim_cur >= REQUIRED_MEMLOCK_SZ;
|
||||
}
|
||||
|
||||
#define RDMA_MIGRATION_HELPER "scripts/rdma-migration-helper.sh"
|
||||
static int new_rdma_link(char *buffer)
|
||||
static int new_rdma_link(char *buffer, bool ipv6)
|
||||
{
|
||||
char cmd[256];
|
||||
bool verbose = g_getenv("QTEST_LOG");
|
||||
|
||||
snprintf(cmd, sizeof(cmd), "%s detect %s", RDMA_MIGRATION_HELPER,
|
||||
snprintf(cmd, sizeof(cmd), "IP_FAMILY=%s %s detect %s",
|
||||
ipv6 ? "ipv6" : "ipv4", RDMA_MIGRATION_HELPER,
|
||||
verbose ? "" : "2>/dev/null");
|
||||
|
||||
FILE *pipe = popen(cmd, "r");
|
||||
@@ -132,11 +162,16 @@ static int new_rdma_link(char *buffer)
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void test_precopy_rdma_plain(void)
|
||||
static void __test_precopy_rdma_plain(bool ipv6)
|
||||
{
|
||||
char buffer[128] = {};
|
||||
|
||||
if (new_rdma_link(buffer)) {
|
||||
if (!mlock_check()) {
|
||||
g_test_skip("'ulimit -l' is too small, require >=128M");
|
||||
return;
|
||||
}
|
||||
|
||||
if (new_rdma_link(buffer, ipv6)) {
|
||||
g_test_skip("No rdma link available\n"
|
||||
"# To enable the test:\n"
|
||||
"# Run \'" RDMA_MIGRATION_HELPER " setup\' with root to "
|
||||
@@ -159,6 +194,16 @@ static void test_precopy_rdma_plain(void)
|
||||
|
||||
test_precopy_common(&args);
|
||||
}
|
||||
|
||||
static void test_precopy_rdma_plain(void)
|
||||
{
|
||||
__test_precopy_rdma_plain(false);
|
||||
}
|
||||
|
||||
static void test_precopy_rdma_plain_ipv6(void)
|
||||
{
|
||||
__test_precopy_rdma_plain(true);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void test_precopy_tcp_plain(void)
|
||||
@@ -524,7 +569,7 @@ static void test_multifd_tcp_channels_none(void)
|
||||
*
|
||||
* And see that it works
|
||||
*/
|
||||
static void test_multifd_tcp_cancel(void)
|
||||
static void test_multifd_tcp_cancel(bool postcopy_ram)
|
||||
{
|
||||
MigrateStart args = {
|
||||
.hide_stderr = true,
|
||||
@@ -538,6 +583,11 @@ static void test_multifd_tcp_cancel(void)
|
||||
migrate_ensure_non_converge(from);
|
||||
migrate_prepare_for_dirty_mem(from);
|
||||
|
||||
if (postcopy_ram) {
|
||||
migrate_set_capability(from, "postcopy-ram", true);
|
||||
migrate_set_capability(to, "postcopy-ram", true);
|
||||
}
|
||||
|
||||
migrate_set_parameter_int(from, "multifd-channels", 16);
|
||||
migrate_set_parameter_int(to, "multifd-channels", 16);
|
||||
|
||||
@@ -579,6 +629,10 @@ static void test_multifd_tcp_cancel(void)
|
||||
return;
|
||||
}
|
||||
|
||||
if (postcopy_ram) {
|
||||
migrate_set_capability(to2, "postcopy-ram", true);
|
||||
}
|
||||
|
||||
migrate_set_parameter_int(to2, "multifd-channels", 16);
|
||||
|
||||
migrate_set_capability(to2, "multifd", true);
|
||||
@@ -602,6 +656,16 @@ static void test_multifd_tcp_cancel(void)
|
||||
migrate_end(from, to2, true);
|
||||
}
|
||||
|
||||
static void test_multifd_precopy_tcp_cancel(void)
|
||||
{
|
||||
test_multifd_tcp_cancel(false);
|
||||
}
|
||||
|
||||
static void test_multifd_postcopy_tcp_cancel(void)
|
||||
{
|
||||
test_multifd_tcp_cancel(true);
|
||||
}
|
||||
|
||||
static void test_cancel_src_after_failed(QTestState *from, QTestState *to,
|
||||
const char *uri, const char *phase)
|
||||
{
|
||||
@@ -1188,10 +1252,17 @@ static void migration_test_add_precopy_smoke(MigrationTestEnv *env)
|
||||
migration_test_add("/migration/multifd/tcp/uri/plain/none",
|
||||
test_multifd_tcp_uri_none);
|
||||
migration_test_add("/migration/multifd/tcp/plain/cancel",
|
||||
test_multifd_tcp_cancel);
|
||||
test_multifd_precopy_tcp_cancel);
|
||||
if (env->has_uffd) {
|
||||
migration_test_add("/migration/multifd+postcopy/tcp/plain/cancel",
|
||||
test_multifd_postcopy_tcp_cancel);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_RDMA
|
||||
migration_test_add("/migration/precopy/rdma/plain",
|
||||
test_precopy_rdma_plain);
|
||||
migration_test_add("/migration/precopy/rdma/plain/ipv6",
|
||||
test_precopy_rdma_plain_ipv6);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -395,6 +395,19 @@ static void test_postcopy_recovery_tls_psk(void)
|
||||
test_postcopy_recovery_common(&args);
|
||||
}
|
||||
|
||||
static void test_multifd_postcopy_recovery_tls_psk(void)
|
||||
{
|
||||
MigrateCommon args = {
|
||||
.start_hook = migrate_hook_start_tls_psk_match,
|
||||
.end_hook = migrate_hook_end_tls_psk,
|
||||
.start = {
|
||||
.caps[MIGRATION_CAPABILITY_MULTIFD] = true,
|
||||
},
|
||||
};
|
||||
|
||||
test_postcopy_recovery_common(&args);
|
||||
}
|
||||
|
||||
/* This contains preempt+recovery+tls test altogether */
|
||||
static void test_postcopy_preempt_all(void)
|
||||
{
|
||||
@@ -409,6 +422,20 @@ static void test_postcopy_preempt_all(void)
|
||||
test_postcopy_recovery_common(&args);
|
||||
}
|
||||
|
||||
static void test_multifd_postcopy_preempt_recovery_tls_psk(void)
|
||||
{
|
||||
MigrateCommon args = {
|
||||
.start_hook = migrate_hook_start_tls_psk_match,
|
||||
.end_hook = migrate_hook_end_tls_psk,
|
||||
.start = {
|
||||
.caps[MIGRATION_CAPABILITY_MULTIFD] = true,
|
||||
.caps[MIGRATION_CAPABILITY_POSTCOPY_PREEMPT] = true,
|
||||
},
|
||||
};
|
||||
|
||||
test_postcopy_recovery_common(&args);
|
||||
}
|
||||
|
||||
static void test_precopy_unix_tls_psk(void)
|
||||
{
|
||||
g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs);
|
||||
@@ -657,6 +684,21 @@ static void test_multifd_tcp_tls_psk_mismatch(void)
|
||||
test_precopy_common(&args);
|
||||
}
|
||||
|
||||
static void test_multifd_postcopy_tcp_tls_psk_match(void)
|
||||
{
|
||||
MigrateCommon args = {
|
||||
.start = {
|
||||
.caps[MIGRATION_CAPABILITY_MULTIFD] = true,
|
||||
.caps[MIGRATION_CAPABILITY_POSTCOPY_RAM] = true,
|
||||
},
|
||||
.listen_uri = "defer",
|
||||
.start_hook = migrate_hook_start_multifd_tcp_tls_psk_match,
|
||||
.end_hook = migrate_hook_end_tls_psk,
|
||||
};
|
||||
|
||||
test_precopy_common(&args);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_TASN1
|
||||
static void test_multifd_tcp_tls_x509_default_host(void)
|
||||
{
|
||||
@@ -774,6 +816,11 @@ void migration_test_add_tls(MigrationTestEnv *env)
|
||||
test_postcopy_preempt_tls_psk);
|
||||
migration_test_add("/migration/postcopy/preempt/recovery/tls/psk",
|
||||
test_postcopy_preempt_all);
|
||||
migration_test_add("/migration/multifd+postcopy/recovery/tls/psk",
|
||||
test_multifd_postcopy_recovery_tls_psk);
|
||||
migration_test_add(
|
||||
"/migration/multifd+postcopy/preempt/recovery/tls/psk",
|
||||
test_multifd_postcopy_preempt_recovery_tls_psk);
|
||||
}
|
||||
#ifdef CONFIG_TASN1
|
||||
migration_test_add("/migration/precopy/unix/tls/x509/default-host",
|
||||
@@ -805,6 +852,10 @@ void migration_test_add_tls(MigrationTestEnv *env)
|
||||
test_multifd_tcp_tls_psk_match);
|
||||
migration_test_add("/migration/multifd/tcp/tls/psk/mismatch",
|
||||
test_multifd_tcp_tls_psk_mismatch);
|
||||
if (env->has_uffd) {
|
||||
migration_test_add("/migration/multifd+postcopy/tcp/tls/psk/match",
|
||||
test_multifd_postcopy_tcp_tls_psk_match);
|
||||
}
|
||||
#ifdef CONFIG_TASN1
|
||||
migration_test_add("/migration/multifd/tcp/tls/x509/default-host",
|
||||
test_multifd_tcp_tls_x509_default_host);
|
||||
|
||||
Reference in New Issue
Block a user