:p
atchew
Login
The following changes since commit 8dd5bceb2f9cc58481e9d22355a8d998220896de: Open 11.0 development tree (2025-12-23 14:45:38 +1100) are available in the Git repository at: https://gitlab.com/peterx/qemu.git tags/next-pull-request for you to fetch changes up to bcb411a005fdf39b76e99c14f3618c7b70f7774d: MAINTAINERS: remove David from "Memory API" section (2025-12-23 09:27:02 -0500) ---------------------------------------------------------------- memory + migration pull - Pawel's misc fixes to mapped-ram when x-ignore-share is enabled - Peter's series to cleanup migration error reporting - Peter's added debug property for x-ignore-shared - Part of Fabiano's series on unify capabilities and parameters - Chuang's log_clear optimization on unaligned ramblocks - Maintainer file update from Ben (CPR++) and David (MemoryAPI-) ---------------------------------------------------------------- Ben Chaney (1): MAINTAINERS: Update reviewers for CPR Chuang Xu (1): migration: merge fragmented clear_dirty ioctls David Hildenbrand (Red Hat) (1): MAINTAINERS: remove David from "Memory API" section Fabiano Rosas (15): migration: Fix leak of block_bitmap_mapping migration: Fix leak of cpr_exec_command migration: Add a qdev property for StrOrNull tests/qtest/migration: Add a NULL parameters test for TLS migration: Normalize tls arguments migration: Remove MigrateSetParameters qapi/migration: Don't document MigrationParameter migration: Run a post update routine after setting parameters migration: Add a flag to track block-bitmap-mapping input migration: Remove checks for s->parameters has_* fields migration: Do away with usage of QERR_INVALID_PARAMETER_VALUE migration: Extract code to mark all parameters as present migration: Use QAPI_CLONE_MEMBERS in query_migrate_parameters tests/qtest/migration: Pass MigrateCommon into test functions tests/qtest/migration: Pass MigrateStart into cancel tests Markus Armbruster (1): error: Poison g_autoptr(Error) to prevent its use Pawel Zmarzly (4): migration: fix parsing snapshots with x-ignore-shared flag migration: Fix writing mapped_ram + ignore_shared snapshots scripts/analyze-migration: Rename RAM_SAVE_FLAG_COMPRESS to RAM_SAVE_FLAG_ZERO scripts/analyze-migration: Support mapped-ram snapshot format Peter Xu (8): migration: Use explicit error_free() instead of g_autoptr Revert "error: define g_autoptr() cleanup function for the Error type" migration: Make migration_connect_set_error() own the error migration: Make multifd_send_set_error() own the error migration: Make multifd_recv_terminate_threads() own the error migration: Replace migrate_set_error() with migrate_error_propagate() migration: Use error_propagate() in migrate_error_propagate() migration/options: Add x-ignore-shared MAINTAINERS | 3 +- qapi/migration.json | 395 +--------------- qapi/pragma.json | 1 + include/qapi/error.h | 20 +- include/system/physmem.h | 7 +- migration/migration.h | 9 +- migration/options.h | 1 + tests/qtest/migration/migration-qmp.h | 1 + tests/qtest/migration/migration-util.h | 8 +- accel/tcg/cputlb.c | 5 +- migration/channel.c | 1 - migration/cpr-exec.c | 5 +- migration/migration-hmp-cmds.c | 13 +- migration/migration.c | 59 ++- migration/multifd-device-state.c | 6 +- migration/multifd.c | 30 +- migration/options.c | 525 ++++++++++++---------- migration/page_cache.c | 6 +- migration/postcopy-ram.c | 5 +- migration/ram.c | 102 +++-- migration/savevm.c | 18 +- migration/tls.c | 2 +- system/memory.c | 2 +- system/physmem.c | 48 +- tests/qtest/migration/compression-tests.c | 131 +++--- tests/qtest/migration/cpr-tests.c | 75 ++-- tests/qtest/migration/file-tests.c | 202 ++++----- tests/qtest/migration/migration-qmp.c | 9 + tests/qtest/migration/migration-util.c | 26 +- tests/qtest/migration/misc-tests.c | 112 ++--- tests/qtest/migration/postcopy-tests.c | 82 ++-- tests/qtest/migration/precopy-tests.c | 373 +++++++-------- tests/qtest/migration/tls-tests.c | 499 ++++++++++---------- scripts/analyze-migration.py | 72 ++- 34 files changed, 1282 insertions(+), 1571 deletions(-) -- 2.50.1
From: Pawel Zmarzly <pzmarzly0@gmail.com> Snapshots made with mapped-ram and x-ignore-shared flags are not parsed properly. The ignore-shared feature adds and extra field in the stream, which needs to be consumed on the destination side. Even though mapped-ram has a fixed header format, the ignore-shared is part of the "generic" stream infomation so the mapped-ram code is currently skipping that be64 read which incorrectly offsets every subsequent read from the stream. The current ignore-shared handling can simply be moved earlier in the code to encompass mapped-ram as well since the ignore-shared doubleword is the first one read when parsing the ramblock section of the stream. Co-authored-by: Peter Xu <peterx@redhat.com> Signed-off-by: Pawel Zmarzly <pzmarzly0@gmail.com> Reviewed-by: Fabiano Rosas <farosas@suse.de> Link: https://lore.kernel.org/r/20251126121233.542473-1-pzmarzly0@gmail.com [peterx: enhance commit log per fabiano] Signed-off-by: Peter Xu <peterx@redhat.com> --- migration/ram.c | 21 +++++++++++---------- tests/qtest/migration/file-tests.c | 18 ++++++++++++++++++ 2 files changed, 29 insertions(+), 10 deletions(-) diff --git a/migration/ram.c b/migration/ram.c index XXXXXXX..XXXXXXX 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -XXX,XX +XXX,XX @@ static int parse_ramblock(QEMUFile *f, RAMBlock *block, ram_addr_t length) assert(block); + if (migrate_ignore_shared()) { + hwaddr addr = qemu_get_be64(f); + if (migrate_ram_is_ignored(block) && + block->mr->addr != addr) { + error_report("Mismatched GPAs for block %s " + "%" PRId64 "!= %" PRId64, block->idstr, + (uint64_t)addr, (uint64_t)block->mr->addr); + return -EINVAL; + } + } + if (migrate_mapped_ram()) { parse_ramblock_mapped_ram(f, block, length, &local_err); if (local_err) { @@ -XXX,XX +XXX,XX @@ static int parse_ramblock(QEMUFile *f, RAMBlock *block, ram_addr_t length) return -EINVAL; } } - if (migrate_ignore_shared()) { - hwaddr addr = qemu_get_be64(f); - if (migrate_ram_is_ignored(block) && - block->mr->addr != addr) { - error_report("Mismatched GPAs for block %s " - "%" PRId64 "!= %" PRId64, block->idstr, - (uint64_t)addr, (uint64_t)block->mr->addr); - return -EINVAL; - } - } ret = rdma_block_notification_handle(f, block->idstr); if (ret < 0) { qemu_file_set_error(f, ret); diff --git a/tests/qtest/migration/file-tests.c b/tests/qtest/migration/file-tests.c index XXXXXXX..XXXXXXX 100644 --- a/tests/qtest/migration/file-tests.c +++ b/tests/qtest/migration/file-tests.c @@ -XXX,XX +XXX,XX @@ static void migration_test_add_file_smoke(MigrationTestEnv *env) test_multifd_file_mapped_ram_dio); } +static void test_precopy_file_mapped_ram_ignore_shared(void) +{ + g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs, + FILE_TEST_FILENAME); + MigrateCommon args = { + .connect_uri = uri, + .listen_uri = "defer", + .start = { + .caps[MIGRATION_CAPABILITY_MAPPED_RAM] = true, + .caps[MIGRATION_CAPABILITY_X_IGNORE_SHARED] = true, + }, + }; + + test_file_common(&args, true); +} + void migration_test_add_file(MigrationTestEnv *env) { tmpfs = env->tmpfs; @@ -XXX,XX +XXX,XX @@ void migration_test_add_file(MigrationTestEnv *env) migration_test_add("/migration/multifd/file/mapped-ram", test_multifd_file_mapped_ram); + migration_test_add("/migration/multifd/file/mapped-ram/ignore-shared", + test_precopy_file_mapped_ram_ignore_shared); migration_test_add("/migration/multifd/file/mapped-ram/live", test_multifd_file_mapped_ram_live); -- 2.50.1
From: Pawel Zmarzly <pzmarzly0@gmail.com> Currently if you set these flags and have any shared memory object, saving a snapshot will fail with: Failed to write bitmap to file: Unable to write to file: Bad address We need to skip writing RAMBlocks that are backed by shared objects. Also, we should mark these RAMBlocks as skipped, so the snapshot format stays readable to tools that later don't know QEMU's command line (for example scripts/analyze-migration.py). I used bitmap_offset=0 pages_offset=0 for this. This minor change to snapshot format should be safe, as offset=0 should not have ever been possible. Signed-off-by: Pawel Zmarzly <pzmarzly0@gmail.com> Link: https://lore.kernel.org/r/20251126154734.940066-1-pzmarzly0@gmail.com Signed-off-by: Peter Xu <peterx@redhat.com> --- migration/ram.c | 53 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 35 insertions(+), 18 deletions(-) diff --git a/migration/ram.c b/migration/ram.c index XXXXXXX..XXXXXXX 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -XXX,XX +XXX,XX @@ static void mapped_ram_setup_ramblock(QEMUFile *file, RAMBlock *block) header = g_new0(MappedRamHeader, 1); header_size = sizeof(MappedRamHeader); - num_pages = block->used_length >> TARGET_PAGE_BITS; - bitmap_size = BITS_TO_LONGS(num_pages) * sizeof(unsigned long); - - /* - * Save the file offsets of where the bitmap and the pages should - * go as they are written at the end of migration and during the - * iterative phase, respectively. - */ - block->bitmap_offset = qemu_get_offset(file) + header_size; - block->pages_offset = ROUND_UP(block->bitmap_offset + - bitmap_size, - MAPPED_RAM_FILE_OFFSET_ALIGNMENT); - header->version = cpu_to_be32(MAPPED_RAM_HDR_VERSION); header->page_size = cpu_to_be64(TARGET_PAGE_SIZE); - header->bitmap_offset = cpu_to_be64(block->bitmap_offset); - header->pages_offset = cpu_to_be64(block->pages_offset); + + if (migrate_ram_is_ignored(block)) { + header->bitmap_offset = 0; + header->pages_offset = 0; + } else { + num_pages = block->used_length >> TARGET_PAGE_BITS; + bitmap_size = BITS_TO_LONGS(num_pages) * sizeof(unsigned long); + + /* + * Save the file offsets of where the bitmap and the pages should + * go as they are written at the end of migration and during the + * iterative phase, respectively. + */ + block->bitmap_offset = qemu_get_offset(file) + header_size; + block->pages_offset = ROUND_UP(block->bitmap_offset + + bitmap_size, + MAPPED_RAM_FILE_OFFSET_ALIGNMENT); + + header->bitmap_offset = cpu_to_be64(block->bitmap_offset); + header->pages_offset = cpu_to_be64(block->pages_offset); + } qemu_put_buffer(file, (uint8_t *) header, header_size); - /* prepare offset for next ramblock */ - qemu_set_offset(file, block->pages_offset + block->used_length, SEEK_SET); + if (!migrate_ram_is_ignored(block)) { + /* leave space for block data */ + qemu_set_offset(file, block->pages_offset + block->used_length, + SEEK_SET); + } } static bool mapped_ram_read_header(QEMUFile *file, MappedRamHeader *header, @@ -XXX,XX +XXX,XX @@ static int ram_save_setup(QEMUFile *f, void *opaque, Error **errp) if (migrate_ignore_shared()) { qemu_put_be64(f, block->mr->addr); } - if (migrate_mapped_ram()) { mapped_ram_setup_ramblock(f, block); } @@ -XXX,XX +XXX,XX @@ static void ram_save_file_bmap(QEMUFile *f) RAMBlock *block; RAMBLOCK_FOREACH_MIGRATABLE(block) { + if (migrate_ram_is_ignored(block)) { + continue; + } + long num_pages = block->used_length >> TARGET_PAGE_BITS; long bitmap_size = BITS_TO_LONGS(num_pages) * sizeof(unsigned long); @@ -XXX,XX +XXX,XX @@ static void parse_ramblock_mapped_ram(QEMUFile *f, RAMBlock *block, return; } + if (migrate_ignore_shared() && + header.bitmap_offset == 0 && header.pages_offset == 0) { + return; + } + block->pages_offset = header.pages_offset; /* -- 2.50.1
From: Pawel Zmarzly <pzmarzly0@gmail.com> It has been renamed on the C side a few years ago. In modern QEMU versions, fill_byte must be zero. Updating the Python script to make grepping and understanding the code easier. Signed-off-by: Pawel Zmarzly <pzmarzly0@gmail.com> Link: https://lore.kernel.org/r/20251125173007.245607-1-pzmarzly0@gmail.com [peterx: fix over-long line] Signed-off-by: Peter Xu <peterx@redhat.com> --- scripts/analyze-migration.py | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/scripts/analyze-migration.py b/scripts/analyze-migration.py index XXXXXXX..XXXXXXX 100755 --- a/scripts/analyze-migration.py +++ b/scripts/analyze-migration.py @@ -XXX,XX +XXX,XX @@ def close(self): self.file.close() class RamSection(object): - RAM_SAVE_FLAG_COMPRESS = 0x02 + RAM_SAVE_FLAG_ZERO = 0x02 RAM_SAVE_FLAG_MEM_SIZE = 0x04 RAM_SAVE_FLAG_PAGE = 0x08 RAM_SAVE_FLAG_EOS = 0x10 @@ -XXX,XX +XXX,XX @@ def read(self): mr_addr = self.file.read64() flags &= ~self.RAM_SAVE_FLAG_MEM_SIZE - if flags & self.RAM_SAVE_FLAG_COMPRESS: + if flags & self.RAM_SAVE_FLAG_ZERO: if flags & self.RAM_SAVE_FLAG_CONTINUE: flags &= ~self.RAM_SAVE_FLAG_CONTINUE else: self.name = self.file.readstr() - fill_char = self.file.read8() - # The page in question is filled with fill_char now - if self.write_memory and fill_char != 0: - self.files[self.name].seek(addr, os.SEEK_SET) - self.files[self.name].write(chr(fill_char) * self.TARGET_PAGE_SIZE) + _fill_char = self.file.read8() if self.dump_memory: - self.memory['%s (0x%016x)' % (self.name, addr)] = 'Filled with 0x%02x' % fill_char - flags &= ~self.RAM_SAVE_FLAG_COMPRESS + self.memory['%s (0x%016x)' % + (self.name, addr)] = 'Filled with 0x00' + flags &= ~self.RAM_SAVE_FLAG_ZERO elif flags & self.RAM_SAVE_FLAG_PAGE: if flags & self.RAM_SAVE_FLAG_CONTINUE: flags &= ~self.RAM_SAVE_FLAG_CONTINUE -- 2.50.1
From: Pawel Zmarzly <pzmarzly0@gmail.com> The script has not been updated to read mapped-ram snapshots and is currently crashing when trying to read such a file. With this commit, it can now read a snapshot created with: (qemu) migrate_set_capability x-ignore-shared on (qemu) migrate_set_capability mapped-ram on (qemu) migrate -d file:vm.state Signed-off-by: Pawel Zmarzly <pzmarzly0@gmail.com> Link: https://lore.kernel.org/r/20251126155015.941129-1-pzmarzly0@gmail.com [peterx: space fixes, introduce parseMappedRamBlob(), add comments, etc.] Signed-off-by: Peter Xu <peterx@redhat.com> --- scripts/analyze-migration.py | 57 ++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/scripts/analyze-migration.py b/scripts/analyze-migration.py index XXXXXXX..XXXXXXX 100755 --- a/scripts/analyze-migration.py +++ b/scripts/analyze-migration.py @@ -XXX,XX +XXX,XX @@ import json import os +import math import argparse import collections import struct @@ -XXX,XX +XXX,XX @@ def __init__(self, file, version_id, ramargs, section_key): self.dump_memory = ramargs['dump_memory'] self.write_memory = ramargs['write_memory'] self.ignore_shared = ramargs['ignore_shared'] + self.mapped_ram = ramargs['mapped_ram'] self.sizeinfo = collections.OrderedDict() self.data = collections.OrderedDict() self.data['section sizes'] = self.sizeinfo @@ -XXX,XX +XXX,XX @@ def __str__(self): def getDict(self): return self.data + def parseMappedRamBlob(self, len): + version = self.file.read32() + if version != 1: + raise Exception("Unsupported MappedRamHeader version %s" % version) + + page_size = self.file.read64() + if page_size != self.TARGET_PAGE_SIZE: + raise Exception("Page size mismatch in MappedRamHeader") + + bitmap_offset = self.file.read64() + pages_offset = self.file.read64() + + if self.ignore_shared and bitmap_offset == 0 and pages_offset == 0: + # This is a shared ramblock, x-ignore-share must have been + # enabled, and mapped-ram didn't allocate bitmap or page blob + # for it. + return + + if self.dump_memory or self.write_memory: + num_pages = len // page_size + + self.file.seek(bitmap_offset, os.SEEK_SET) + bitmap_len = int(math.ceil(num_pages / 8)) + bitmap = self.file.readvar(size=bitmap_len) + + self.file.seek(pages_offset, os.SEEK_SET) + for page_num in range(num_pages): + page_addr = page_num * page_size + + is_filled = (bitmap[page_num // 8] >> page_num % 8) & 1 + if is_filled: + data = self.file.readvar(size=self.TARGET_PAGE_SIZE) + if self.write_memory: + self.files[self.name].seek(page_addr, os.SEEK_SET) + self.files[self.name].write(data) + if self.dump_memory: + hexdata = " ".join("{0:02x}".format(c) for c in data) + self.memory['%s (0x%016x)' % + (self.name, page_addr)] = hexdata + else: + self.file.seek(self.TARGET_PAGE_SIZE, os.SEEK_CUR) + if self.write_memory: + self.files[self.name].seek(page_addr, os.SEEK_SET) + self.files[self.name].write( + b'\x00' * self.TARGET_PAGE_SIZE) + if self.dump_memory: + self.memory['%s (0x%016x)' % + (self.name, page_addr)] = 'Filled with 0x00' + + self.file.seek(pages_offset + len, os.SEEK_SET) + def read(self): # Read all RAM sections while True: @@ -XXX,XX +XXX,XX @@ def read(self): self.files[self.name] = f if self.ignore_shared: mr_addr = self.file.read64() + if self.mapped_ram: + self.parseMappedRamBlob(len) flags &= ~self.RAM_SAVE_FLAG_MEM_SIZE if flags & self.RAM_SAVE_FLAG_ZERO: @@ -XXX,XX +XXX,XX @@ def read(self, desc_only = False, dump_memory = False, ramargs['dump_memory'] = dump_memory ramargs['write_memory'] = write_memory ramargs['ignore_shared'] = False + ramargs['mapped_ram'] = False self.section_classes[('ram',0)][1] = ramargs while True: @@ -XXX,XX +XXX,XX @@ def read(self, desc_only = False, dump_memory = False, section = ConfigurationSection(file, config_desc) section.read() ramargs['ignore_shared'] = section.has_capability('x-ignore-shared') + ramargs['mapped_ram'] = section.has_capability('mapped-ram') elif section_type == self.QEMU_VM_SECTION_START or section_type == self.QEMU_VM_SECTION_FULL: section_id = file.read32() name = file.readstr() -- 2.50.1
There're only two use cases of g_autoptr to free Error objects in migration code paths. Due to the nature of how Error should be used (normally ownership will be passed over to Error APIs, like error_report_err), auto-free functions may be error prone on its own. The auto cleanup function was merged without proper review, as pointed out by Dan and Markus: https://lore.kernel.org/r/aSWSLMi6ZhTCS_p2@redhat.com Remove the two use cases so that we can remove the auto cleanup function, hence suggest to not use auto frees for Errors. Suggested-by: Markus Armbruster <armbru@redhat.com> Reviewed-by: Markus Armbruster <armbru@redhat.com> Link: https://lore.kernel.org/r/20251201194510.1121221-2-peterx@redhat.com Signed-off-by: Peter Xu <peterx@redhat.com> --- migration/multifd-device-state.c | 3 ++- migration/savevm.c | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/migration/multifd-device-state.c b/migration/multifd-device-state.c index XXXXXXX..XXXXXXX 100644 --- a/migration/multifd-device-state.c +++ b/migration/multifd-device-state.c @@ -XXX,XX +XXX,XX @@ static void multifd_device_state_save_thread_data_free(void *opaque) static int multifd_device_state_save_thread(void *opaque) { SaveCompletePrecopyThreadData *data = opaque; - g_autoptr(Error) local_err = NULL; + Error *local_err = NULL; if (!data->hdlr(data, &local_err)) { MigrationState *s = migrate_get_current(); @@ -XXX,XX +XXX,XX @@ static int multifd_device_state_save_thread(void *opaque) * return we end setting is purely arbitrary. */ migrate_set_error(s, local_err); + error_free(local_err); } return 0; diff --git a/migration/savevm.c b/migration/savevm.c index XXXXXXX..XXXXXXX 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -XXX,XX +XXX,XX @@ static int qemu_loadvm_load_thread(void *thread_opaque) { struct LoadThreadData *data = thread_opaque; MigrationIncomingState *mis = migration_incoming_get_current(); - g_autoptr(Error) local_err = NULL; + Error *local_err = NULL; if (!data->function(data->opaque, &mis->load_threads_abort, &local_err)) { MigrationState *s = migrate_get_current(); @@ -XXX,XX +XXX,XX @@ static int qemu_loadvm_load_thread(void *thread_opaque) * return we end setting is purely arbitrary. */ migrate_set_error(s, local_err); + error_free(local_err); } return 0; -- 2.50.1
This reverts commit 18eb55546a54e443d94a4c49286348176ad4b00a. Due to the nature of how Error should be used (normally ownership will be passed over to Error APIs, like error_report_err), auto-free functions may be error prone on its own. The auto cleanup function was merged without proper review as pointed out by Dan and Markus: https://lore.kernel.org/r/aSWSLMi6ZhTCS_p2@redhat.com Cc: Cédric Le Goater <clg@redhat.com> Acked-by: Maciej S. Szmigiero <mail@maciej.szmigiero.name> Reviewed-by: Markus Armbruster <armbru@redhat.com> Reviewed-by: Cédric Le Goater <clg@redhat.com> Acked-by: Maciej S. Szmigiero <maciej.szmigiero@oracle.com> Link: https://lore.kernel.org/r/20251201194510.1121221-3-peterx@redhat.com Signed-off-by: Peter Xu <peterx@redhat.com> --- include/qapi/error.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/include/qapi/error.h b/include/qapi/error.h index XXXXXXX..XXXXXXX 100644 --- a/include/qapi/error.h +++ b/include/qapi/error.h @@ -XXX,XX +XXX,XX @@ Error *error_copy(const Error *err); */ void error_free(Error *err); -G_DEFINE_AUTOPTR_CLEANUP_FUNC(Error, error_free) - /* * Convenience function to assert that *@errp is set, then silently free it. */ -- 2.50.1
From: Markus Armbruster <armbru@redhat.com> The previous commit reverted support for g_autoptr(Error). This one should stop it from coming back. Suggested-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Markus Armbruster <armbru@redhat.com> Tested-by: Cédric Le Goater <clg@redhat.com> Reviewed-by: Cédric Le Goater <clg@redhat.com> Link: https://lore.kernel.org/r/20251201194510.1121221-4-peterx@redhat.com Signed-off-by: Peter Xu <peterx@redhat.com> --- include/qapi/error.h | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/include/qapi/error.h b/include/qapi/error.h index XXXXXXX..XXXXXXX 100644 --- a/include/qapi/error.h +++ b/include/qapi/error.h @@ -XXX,XX +XXX,XX @@ Error *error_copy(const Error *err); */ void error_free(Error *err); +/* + * Poison g_autoptr(Error) to prevent its use. + * + * Functions that report or propagate an error take ownership of the + * Error object. Explicit error_free() is needed when you handle an + * error in some other way. This is rare. + * + * g_autoptr(Error) would call error_free() automatically on return. + * To avoid a double-free, we'd have to manually clear the pointer + * every time we propagate or report. + * + * Thus, g_autoptr(Error) would make the rare case easier to get right + * (less prone to leaks), and the common case easier to get wrong + * (more prone to double-free). + */ +extern void +__attribute__((error("Do not use g_autoptr() to declare Error * variables"))) +error_free_poisoned(Error *err); +G_DEFINE_AUTOPTR_CLEANUP_FUNC(Error, error_free_poisoned) + /* * Convenience function to assert that *@errp is set, then silently free it. */ -- 2.50.1
Make migration_connect_set_error() take ownership of the error always. Paving way for making migrate_set_error() to take ownership. When at it, renaming it to migration_connect_error_propagate(), following Error API, to imply the Error object ownership transition. NOTE: this patch also makes migration_connect() to take ownership of the Error passed in. Reviewed-by: Markus Armbruster <armbru@redhat.com> Link: https://lore.kernel.org/r/20251201194510.1121221-5-peterx@redhat.com Signed-off-by: Peter Xu <peterx@redhat.com> --- migration/channel.c | 1 - migration/migration.c | 9 +++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/migration/channel.c b/migration/channel.c index XXXXXXX..XXXXXXX 100644 --- a/migration/channel.c +++ b/migration/channel.c @@ -XXX,XX +XXX,XX @@ void migration_channel_connect(MigrationState *s, } } migration_connect(s, error); - error_free(error); } diff --git a/migration/migration.c b/migration/migration.c index XXXXXXX..XXXXXXX 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -XXX,XX +XXX,XX @@ static void migrate_error_free(MigrationState *s) } } -static void migration_connect_set_error(MigrationState *s, const Error *error) +static void migration_connect_error_propagate(MigrationState *s, Error *error) { MigrationStatus current = s->state; MigrationStatus next; @@ -XXX,XX +XXX,XX @@ static void migration_connect_set_error(MigrationState *s, const Error *error) migrate_set_state(&s->state, current, next); migrate_set_error(s, error); + error_free(error); } void migration_cancel(void) @@ -XXX,XX +XXX,XX @@ void qmp_migrate(const char *uri, bool has_channels, out: if (local_err) { - migration_connect_set_error(s, local_err); + migration_connect_error_propagate(s, error_copy(local_err)); error_propagate(errp, local_err); } } @@ -XXX,XX +XXX,XX @@ static void qmp_migrate_finish(MigrationAddress *addr, bool resume_requested, if (!resume_requested) { yank_unregister_instance(MIGRATION_YANK_INSTANCE); } - migration_connect_set_error(s, local_err); + migration_connect_error_propagate(s, error_copy(local_err)); error_propagate(errp, local_err); return; } @@ -XXX,XX +XXX,XX @@ void migration_connect(MigrationState *s, Error *error_in) s->expected_downtime = migrate_downtime_limit(); if (error_in) { - migration_connect_set_error(s, error_in); + migration_connect_error_propagate(s, error_in); if (resume) { /* * Don't do cleanup for resume if channel is invalid, but only dump -- 2.50.1
Make multifd_send_set_error() take ownership of the error always. Paving way for making migrate_set_error() to take ownership. When at it, rename it to multifd_send_error_propagate() to imply the ownership transition following Error API's naming style. Reviewed-by: Markus Armbruster <armbru@redhat.com> Link: https://lore.kernel.org/r/20251201194510.1121221-6-peterx@redhat.com Signed-off-by: Peter Xu <peterx@redhat.com> --- migration/multifd.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/migration/multifd.c b/migration/multifd.c index XXXXXXX..XXXXXXX 100644 --- a/migration/multifd.c +++ b/migration/multifd.c @@ -XXX,XX +XXX,XX @@ bool multifd_send(MultiFDSendData **send_data) } /* Multifd send side hit an error; remember it and prepare to quit */ -static void multifd_send_set_error(Error *err) +static void multifd_send_error_propagate(Error *err) { /* * We don't want to exit each threads twice. Depending on where @@ -XXX,XX +XXX,XX @@ static void multifd_send_set_error(Error *err) if (err) { MigrationState *s = migrate_get_current(); migrate_set_error(s, err); + error_free(err); if (s->state == MIGRATION_STATUS_SETUP || s->state == MIGRATION_STATUS_PRE_SWITCHOVER || s->state == MIGRATION_STATUS_DEVICE || @@ -XXX,XX +XXX,XX @@ out: if (ret) { assert(local_err); trace_multifd_send_error(p->id); - multifd_send_set_error(local_err); + multifd_send_error_propagate(local_err); multifd_send_kick_main(p); - error_free(local_err); } rcu_unregister_thread(); @@ -XXX,XX +XXX,XX @@ out: } trace_multifd_new_send_channel_async_error(p->id, local_err); - multifd_send_set_error(local_err); + multifd_send_error_propagate(local_err); /* * For error cases (TLS or non-TLS), IO channel is always freed here * rather than when cleanup multifd: since p->c is not set, multifd * cleanup code doesn't even know its existence. */ object_unref(OBJECT(ioc)); - error_free(local_err); } static bool multifd_new_send_channel_create(gpointer opaque, Error **errp) -- 2.50.1
Make multifd_recv_terminate_threads() take ownership of the error always. Paving way for making migrate_set_error() to take ownership. Reviewed-by: Markus Armbruster <armbru@redhat.com> Link: https://lore.kernel.org/r/20251201194510.1121221-7-peterx@redhat.com Signed-off-by: Peter Xu <peterx@redhat.com> --- migration/multifd.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/migration/multifd.c b/migration/multifd.c index XXXXXXX..XXXXXXX 100644 --- a/migration/multifd.c +++ b/migration/multifd.c @@ -XXX,XX +XXX,XX @@ static void multifd_recv_terminate_threads(Error *err) if (err) { MigrationState *s = migrate_get_current(); migrate_set_error(s, err); + error_free(err); if (s->state == MIGRATION_STATUS_SETUP || s->state == MIGRATION_STATUS_ACTIVE) { migrate_set_state(&s->state, s->state, @@ -XXX,XX +XXX,XX @@ static void *multifd_recv_thread(void *opaque) if (local_err) { multifd_recv_terminate_threads(local_err); - error_free(local_err); } rcu_unregister_thread(); @@ -XXX,XX +XXX,XX @@ void multifd_recv_new_channel(QIOChannel *ioc, Error **errp) if (use_packets) { id = multifd_recv_initial_packet(ioc, &local_err); if (id < 0) { - multifd_recv_terminate_threads(local_err); + multifd_recv_terminate_threads(error_copy(local_err)); error_propagate_prepend(errp, local_err, "failed to receive packet" " via multifd channel %d: ", @@ -XXX,XX +XXX,XX @@ void multifd_recv_new_channel(QIOChannel *ioc, Error **errp) if (p->c != NULL) { error_setg(&local_err, "multifd: received id '%d' already setup'", id); - multifd_recv_terminate_threads(local_err); + multifd_recv_terminate_threads(error_copy(local_err)); error_propagate(errp, local_err); return; } -- 2.50.1
migrate_set_error() currently doesn't take ownership of the error being passed in. It's not aligned with the error API and meanwhile it also makes most of the caller free the error explicitly. Change the API to take the ownership of the Error object instead. This should save a lot of error_copy() invocations. Reviewed-by: Markus Armbruster <armbru@redhat.com> Link: https://lore.kernel.org/r/20251201194510.1121221-8-peterx@redhat.com [peterx: break line for qemu_savevm_send_packaged, per markus] Signed-off-by: Peter Xu <peterx@redhat.com> --- migration/migration.h | 2 +- migration/cpr-exec.c | 5 ++-- migration/migration.c | 44 +++++++++++++++----------------- migration/multifd-device-state.c | 5 +--- migration/multifd.c | 19 +++++++------- migration/postcopy-ram.c | 5 ++-- migration/ram.c | 4 +-- migration/savevm.c | 17 +++++------- 8 files changed, 44 insertions(+), 57 deletions(-) diff --git a/migration/migration.h b/migration/migration.h index XXXXXXX..XXXXXXX 100644 --- a/migration/migration.h +++ b/migration/migration.h @@ -XXX,XX +XXX,XX @@ void migration_incoming_process(void); bool migration_has_all_channels(void); -void migrate_set_error(MigrationState *s, const Error *error); +void migrate_error_propagate(MigrationState *s, Error *error); bool migrate_has_error(MigrationState *s); void migration_connect(MigrationState *s, Error *error_in); diff --git a/migration/cpr-exec.c b/migration/cpr-exec.c index XXXXXXX..XXXXXXX 100644 --- a/migration/cpr-exec.c +++ b/migration/cpr-exec.c @@ -XXX,XX +XXX,XX @@ static void cpr_exec_cb(void *opaque) error_report_err(error_copy(err)); migrate_set_state(&s->state, s->state, MIGRATION_STATUS_FAILED); - migrate_set_error(s, err); - error_free(err); + + migrate_error_propagate(s, err); + /* We must reset the error because it'll be reused later */ err = NULL; /* Note, we can go from state COMPLETED to FAILED */ diff --git a/migration/migration.c b/migration/migration.c index XXXXXXX..XXXXXXX 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -XXX,XX +XXX,XX @@ process_incoming_migration_co(void *opaque) fail: migrate_set_state(&mis->state, MIGRATION_STATUS_ACTIVE, MIGRATION_STATUS_FAILED); - migrate_set_error(s, local_err); - error_free(local_err); - + migrate_error_propagate(s, local_err); migration_incoming_state_destroy(); if (mis->exit_on_error) { @@ -XXX,XX +XXX,XX @@ static void migration_cleanup_bh(void *opaque) migration_cleanup(opaque); } -void migrate_set_error(MigrationState *s, const Error *error) +/* + * Propagate the Error* object to migration core. The caller mustn't + * reference the error pointer after the function returned, because the + * Error* object might be freed. + */ +void migrate_error_propagate(MigrationState *s, Error *error) { QEMU_LOCK_GUARD(&s->error_mutex); - trace_migrate_error(error_get_pretty(error)); if (!s->error) { - s->error = error_copy(error); + s->error = error; + } else { + error_free(error); } } @@ -XXX,XX +XXX,XX @@ static void migration_connect_error_propagate(MigrationState *s, Error *error) } migrate_set_state(&s->state, current, next); - migrate_set_error(s, error); - error_free(error); + migrate_error_propagate(s, error); } void migration_cancel(void) @@ -XXX,XX +XXX,XX @@ void qmp_migrate_pause(Error **errp) /* Tell the core migration that we're pausing */ error_setg(&error, "Postcopy migration is paused by the user"); - migrate_set_error(ms, error); - error_free(error); + migrate_error_propagate(ms, error); qemu_mutex_lock(&ms->qemu_file_lock); if (ms->to_dst_file) { @@ -XXX,XX +XXX,XX @@ static void *source_return_path_thread(void *opaque) out: if (err) { - migrate_set_error(ms, err); - error_free(err); + migrate_error_propagate(ms, err); trace_source_return_path_thread_bad_end(); } @@ -XXX,XX +XXX,XX @@ static void migration_completion(MigrationState *s) fail: if (qemu_file_get_error_obj(s->to_dst_file, &local_err)) { - migrate_set_error(s, local_err); - error_free(local_err); + migrate_error_propagate(s, local_err); } else if (ret) { error_setg_errno(&local_err, -ret, "Error in migration completion"); - migrate_set_error(s, local_err); - error_free(local_err); + migrate_error_propagate(s, local_err); } if (s->state != MIGRATION_STATUS_CANCELLING) { @@ -XXX,XX +XXX,XX @@ static MigThrError migration_detect_error(MigrationState *s) } if (local_error) { - migrate_set_error(s, local_error); - error_free(local_error); + migrate_error_propagate(s, local_error); } if (state == MIGRATION_STATUS_POSTCOPY_ACTIVE && ret) { @@ -XXX,XX +XXX,XX @@ static MigIterateState migration_iteration_run(MigrationState *s) if (must_precopy <= s->threshold_size && can_switchover && qatomic_read(&s->start_postcopy)) { if (postcopy_start(s, &local_err)) { - migrate_set_error(s, local_err); + migrate_error_propagate(s, error_copy(local_err)); error_report_err(local_err); } return MIG_ITERATE_SKIP; @@ -XXX,XX +XXX,XX @@ static void *migration_thread(void *opaque) * devices to unplug. This to preserve migration state transitions. */ if (ret) { - migrate_set_error(s, local_err); - error_free(local_err); + migrate_error_propagate(s, local_err); migrate_set_state(&s->state, MIGRATION_STATUS_ACTIVE, MIGRATION_STATUS_FAILED); goto out; @@ -XXX,XX +XXX,XX @@ static void *bg_migration_thread(void *opaque) * devices to unplug. This to preserve migration state transitions. */ if (ret) { - migrate_set_error(s, local_err); - error_free(local_err); + migrate_error_propagate(s, local_err); migrate_set_state(&s->state, MIGRATION_STATUS_ACTIVE, MIGRATION_STATUS_FAILED); goto fail_setup; @@ -XXX,XX +XXX,XX @@ void migration_connect(MigrationState *s, Error *error_in) return; fail: - migrate_set_error(s, local_err); + migrate_error_propagate(s, error_copy(local_err)); if (s->state != MIGRATION_STATUS_CANCELLING) { migrate_set_state(&s->state, s->state, MIGRATION_STATUS_FAILED); } diff --git a/migration/multifd-device-state.c b/migration/multifd-device-state.c index XXXXXXX..XXXXXXX 100644 --- a/migration/multifd-device-state.c +++ b/migration/multifd-device-state.c @@ -XXX,XX +XXX,XX @@ static int multifd_device_state_save_thread(void *opaque) Error *local_err = NULL; if (!data->hdlr(data, &local_err)) { - MigrationState *s = migrate_get_current(); - /* * Can't call abort_device_state_save_threads() here since new * save threads could still be in process of being launched @@ -XXX,XX +XXX,XX @@ static int multifd_device_state_save_thread(void *opaque) * In case of multiple save threads failing which thread error * return we end setting is purely arbitrary. */ - migrate_set_error(s, local_err); - error_free(local_err); + migrate_error_propagate(migrate_get_current(), local_err); } return 0; diff --git a/migration/multifd.c b/migration/multifd.c index XXXXXXX..XXXXXXX 100644 --- a/migration/multifd.c +++ b/migration/multifd.c @@ -XXX,XX +XXX,XX @@ static void multifd_send_error_propagate(Error *err) if (err) { MigrationState *s = migrate_get_current(); - migrate_set_error(s, err); - error_free(err); + + migrate_error_propagate(s, err); + if (s->state == MIGRATION_STATUS_SETUP || s->state == MIGRATION_STATUS_PRE_SWITCHOVER || s->state == MIGRATION_STATUS_DEVICE || @@ -XXX,XX +XXX,XX @@ void multifd_send_shutdown(void) Error *local_err = NULL; if (!multifd_send_cleanup_channel(p, &local_err)) { - migrate_set_error(migrate_get_current(), local_err); - error_free(local_err); + migrate_error_propagate(migrate_get_current(), local_err); } } @@ -XXX,XX +XXX,XX @@ bool multifd_send_setup(void) p->write_flags = 0; if (!multifd_new_send_channel_create(p, &local_err)) { - migrate_set_error(s, local_err); - error_free(local_err); + migrate_error_propagate(s, local_err); ret = -1; } } @@ -XXX,XX +XXX,XX @@ bool multifd_send_setup(void) ret = multifd_send_state->ops->send_setup(p, &local_err); if (ret) { - migrate_set_error(s, local_err); - error_free(local_err); + migrate_error_propagate(s, local_err); goto err; } assert(p->iov); @@ -XXX,XX +XXX,XX @@ static void multifd_recv_terminate_threads(Error *err) if (err) { MigrationState *s = migrate_get_current(); - migrate_set_error(s, err); - error_free(err); + + migrate_error_propagate(s, err); + if (s->state == MIGRATION_STATUS_SETUP || s->state == MIGRATION_STATUS_ACTIVE) { migrate_set_state(&s->state, s->state, diff --git a/migration/postcopy-ram.c b/migration/postcopy-ram.c index XXXXXXX..XXXXXXX 100644 --- a/migration/postcopy-ram.c +++ b/migration/postcopy-ram.c @@ -XXX,XX +XXX,XX @@ postcopy_preempt_send_channel_done(MigrationState *s, QIOChannel *ioc, Error *local_err) { if (local_err) { - migrate_set_error(s, local_err); - error_free(local_err); + migrate_error_propagate(s, local_err); } else { migration_ioc_register_yank(ioc); s->postcopy_qemufile_src = qemu_file_new_output(ioc); @@ -XXX,XX +XXX,XX @@ static void *postcopy_listen_thread(void *opaque) * exit depending on if postcopy-exit-on-error is true, but the * migration cannot be recovered. */ - migrate_set_error(migr, local_err); + migrate_error_propagate(migr, error_copy(local_err)); error_report_err(local_err); migrate_set_state(&mis->state, mis->state, MIGRATION_STATUS_FAILED); goto out; diff --git a/migration/ram.c b/migration/ram.c index XXXXXXX..XXXXXXX 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -XXX,XX +XXX,XX @@ static void ram_mig_ram_block_resized(RAMBlockNotifier *n, void *host, * Abort and indicate a proper reason. */ error_setg(&err, "RAM block '%s' resized during precopy.", rb->idstr); - migrate_set_error(migrate_get_current(), err); - error_free(err); - + migrate_error_propagate(migrate_get_current(), err); migration_cancel(); } diff --git a/migration/savevm.c b/migration/savevm.c index XXXXXXX..XXXXXXX 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -XXX,XX +XXX,XX @@ void qemu_savevm_send_open_return_path(QEMUFile *f) int qemu_savevm_send_packaged(QEMUFile *f, const uint8_t *buf, size_t len) { uint32_t tmp; - MigrationState *ms = migrate_get_current(); Error *local_err = NULL; if (len > MAX_VM_CMD_PACKAGED_SIZE) { error_setg(&local_err, "%s: Unreasonably large packaged state: %zu", __func__, len); - migrate_set_error(ms, local_err); + migrate_error_propagate(migrate_get_current(), + error_copy(local_err)); error_report_err(local_err); return -1; } @@ -XXX,XX +XXX,XX @@ int qemu_savevm_state_setup(QEMUFile *f, Error **errp) if (se->vmsd && se->vmsd->early_setup) { ret = vmstate_save(f, se, vmdesc, errp); if (ret) { - migrate_set_error(ms, *errp); + migrate_error_propagate(ms, error_copy(*errp)); qemu_file_set_error(f, ret); break; } @@ -XXX,XX +XXX,XX @@ int qemu_savevm_state_complete_precopy_non_iterable(QEMUFile *f, ret = vmstate_save(f, se, vmdesc, &local_err); if (ret) { - migrate_set_error(ms, local_err); + migrate_error_propagate(ms, error_copy(local_err)); error_report_err(local_err); qemu_file_set_error(f, ret); return ret; @@ -XXX,XX +XXX,XX @@ void qemu_savevm_live_state(QEMUFile *f) int qemu_save_device_state(QEMUFile *f) { - MigrationState *ms = migrate_get_current(); Error *local_err = NULL; SaveStateEntry *se; @@ -XXX,XX +XXX,XX @@ int qemu_save_device_state(QEMUFile *f) } ret = vmstate_save(f, se, NULL, &local_err); if (ret) { - migrate_set_error(ms, local_err); + migrate_error_propagate(migrate_get_current(), + error_copy(local_err)); error_report_err(local_err); return ret; } @@ -XXX,XX +XXX,XX @@ static int qemu_loadvm_load_thread(void *thread_opaque) Error *local_err = NULL; if (!data->function(data->opaque, &mis->load_threads_abort, &local_err)) { - MigrationState *s = migrate_get_current(); - /* * Can't set load_threads_abort here since processing of main migration * channel data could still be happening, resulting in launching of new @@ -XXX,XX +XXX,XX @@ static int qemu_loadvm_load_thread(void *thread_opaque) * In case of multiple load threads failing which thread error * return we end setting is purely arbitrary. */ - migrate_set_error(s, local_err); - error_free(local_err); + migrate_error_propagate(migrate_get_current(), local_err); } return 0; -- 2.50.1
It improves readability, as suggested by Markus. Suggested-by: Markus Armbruster <armbru@redhat.com> Reviewed-by: Markus Armbruster <armbru@redhat.com> Link: https://lore.kernel.org/r/20251202175317.1186544-1-peterx@redhat.com Signed-off-by: Peter Xu <peterx@redhat.com> --- migration/migration.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index XXXXXXX..XXXXXXX 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -XXX,XX +XXX,XX @@ void migrate_error_propagate(MigrationState *s, Error *error) { QEMU_LOCK_GUARD(&s->error_mutex); trace_migrate_error(error_get_pretty(error)); - - if (!s->error) { - s->error = error; - } else { - error_free(error); - } + error_propagate(&s->error, error); } bool migrate_has_error(MigrationState *s) -- 2.50.1
This aids scriptings only. Reviewed-by: Fabiano Rosas <farosas@suse.de> Link: https://lore.kernel.org/r/20251205172054.288909-1-peterx@redhat.com [peterx: make the property x-ignore-shared to match, per cedric] [peterx: fix over-80 line] Signed-off-by: Peter Xu <peterx@redhat.com> --- migration/options.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/migration/options.c b/migration/options.c index XXXXXXX..XXXXXXX 100644 --- a/migration/options.c +++ b/migration/options.c @@ -XXX,XX +XXX,XX @@ const Property migration_properties[] = { MIGRATION_CAPABILITY_SWITCHOVER_ACK), DEFINE_PROP_MIG_CAP("x-dirty-limit", MIGRATION_CAPABILITY_DIRTY_LIMIT), DEFINE_PROP_MIG_CAP("mapped-ram", MIGRATION_CAPABILITY_MAPPED_RAM), + DEFINE_PROP_MIG_CAP("x-ignore-shared", + MIGRATION_CAPABILITY_X_IGNORE_SHARED), }; const size_t migration_properties_count = ARRAY_SIZE(migration_properties); -- 2.50.1
From: Ben Chaney <bchaney@akamai.com> Signed-off-by: Ben Chaney <bchaney@akamai.com> Reviewed-by: Fabiano Rosas <farosas@suse.de> Link: https://lore.kernel.org/r/20251210143624.416697-1-bchaney@akamai.com Signed-off-by: Peter Xu <peterx@redhat.com> --- MAINTAINERS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index XXXXXXX..XXXXXXX 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -XXX,XX +XXX,XX @@ T: git https://gitlab.com/vsementsov/qemu.git block CheckPoint and Restart (CPR) R: Peter Xu <peterx@redhat.com> R: Fabiano Rosas <farosas@suse.de> +R: Mark Kanda <mark.kanda@oracle.com> +R: Ben Chaney <bchaney@akamai.com> S: Supported F: hw/vfio/cpr* F: include/hw/vfio/vfio-cpr.h -- 2.50.1
From: Fabiano Rosas <farosas@suse.de> Caught by inspection, but ASAN also reports: Direct leak of 16 byte(s) in 1 object(s) allocated from: #0 in malloc #1 in g_malloc #2 in g_memdup #3 in qapi_clone_start_struct ../qapi/qapi-clone-visitor.c:40:12 #4 in qapi_clone_start_list ../qapi/qapi-clone-visitor.c:59:12 #5 in visit_start_list ../qapi/qapi-visit-core.c:80:10 #6 in visit_type_BitmapMigrationNodeAliasList qapi/qapi-visit-migration.c:639:10 #7 in migrate_params_apply ../migration/options.c:1407:13 #8 in qmp_migrate_set_parameters ../migration/options.c:1463:5 #9 in qmp_marshal_migrate_set_parameters qapi/qapi-commands-migration.c:214:5 #10 in do_qmp_dispatch_bh ../qapi/qmp-dispatch.c:128:5 Note that this is entirely harmless because the migration object which contains the MigrationParameters structure is kept until the QEMU process exits. Reviewed-by: Markus Armbruster <armbru@redhat.com> Reviewed-by: Peter Xu <peterx@redhat.com> Signed-off-by: Fabiano Rosas <farosas@suse.de> Link: https://lore.kernel.org/r/20251215220041.12657-2-farosas@suse.de Signed-off-by: Peter Xu <peterx@redhat.com> --- migration/migration.c | 1 + 1 file changed, 1 insertion(+) diff --git a/migration/migration.c b/migration/migration.c index XXXXXXX..XXXXXXX 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -XXX,XX +XXX,XX @@ static void migration_instance_finalize(Object *obj) { MigrationState *ms = MIGRATION_OBJ(obj); + qapi_free_BitmapMigrationNodeAliasList(ms->parameters.block_bitmap_mapping); qemu_mutex_destroy(&ms->error_mutex); qemu_mutex_destroy(&ms->qemu_file_lock); qemu_sem_destroy(&ms->wait_unplug_sem); -- 2.50.1
From: Fabiano Rosas <farosas@suse.de> Signed-off-by: Fabiano Rosas <farosas@suse.de> Reviewed-by: Peter Xu <peterx@redhat.com> Link: https://lore.kernel.org/r/20251215220041.12657-3-farosas@suse.de Signed-off-by: Peter Xu <peterx@redhat.com> --- migration/migration.c | 1 + 1 file changed, 1 insertion(+) diff --git a/migration/migration.c b/migration/migration.c index XXXXXXX..XXXXXXX 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -XXX,XX +XXX,XX @@ static void migration_instance_finalize(Object *obj) MigrationState *ms = MIGRATION_OBJ(obj); qapi_free_BitmapMigrationNodeAliasList(ms->parameters.block_bitmap_mapping); + qapi_free_strList(ms->parameters.cpr_exec_command); qemu_mutex_destroy(&ms->error_mutex); qemu_mutex_destroy(&ms->qemu_file_lock); qemu_sem_destroy(&ms->wait_unplug_sem); -- 2.50.1
From: Fabiano Rosas <farosas@suse.de> The MigrationState is a QOM object with TYPE_DEVICE as a parent. This was done about eight years ago so the migration code could make use of qdev properties to define the defaults for the migration parameters and to be able to expose migration knobs for debugging via the '-global migration' command line option. Due to unrelated historical reasons, three of the migration parameters (TLS options) received different types when used via the query-migrate-parameters QMP command than with the migrate-set-parameters command. This has created a lot of duplication in the migration code and in the QAPI documentation because the whole of MigrationParameters had to be duplicated as well. The migration code is now being fixed to remove the duplication and for that to happen the offending fields need to be reconciled into a single type. The StrOrNull type is going to be used. To keep the command line compatibility, the parameters need to continue being exposed via qdev properties accessible from the command line. Introduce a qdev property StrOrNull just for that. Note that this code is being kept in migration/options.c as this version of StrOrNull doesn't need to handle QNULL because it was never a valid option in the previous command line, which took a string. Signed-off-by: Fabiano Rosas <farosas@suse.de> Acked-by: Peter Xu <peterx@redhat.com> Link: https://lore.kernel.org/r/20251215220041.12657-4-farosas@suse.de Signed-off-by: Peter Xu <peterx@redhat.com> --- migration/options.c | 67 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/migration/options.c b/migration/options.c index XXXXXXX..XXXXXXX 100644 --- a/migration/options.c +++ b/migration/options.c @@ -XXX,XX +XXX,XX @@ #define DEFINE_PROP_MIG_CAP(name, x) \ DEFINE_PROP_BOOL(name, MigrationState, capabilities[x], false) +const PropertyInfo qdev_prop_StrOrNull; +#define DEFINE_PROP_STR_OR_NULL(_name, _state, _field) \ + DEFINE_PROP(_name, _state, _field, qdev_prop_StrOrNull, StrOrNull *, \ + .set_default = true) + #define DEFAULT_MIGRATE_VCPU_DIRTY_LIMIT_PERIOD 1000 /* milliseconds */ #define DEFAULT_MIGRATE_VCPU_DIRTY_LIMIT 1 /* MB/s */ @@ -XXX,XX +XXX,XX @@ const Property migration_properties[] = { }; const size_t migration_properties_count = ARRAY_SIZE(migration_properties); +static void get_StrOrNull(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + const Property *prop = opaque; + StrOrNull **ptr = object_field_prop_ptr(obj, prop); + StrOrNull *str_or_null = *ptr; + + if (!str_or_null) { + str_or_null = g_new0(StrOrNull, 1); + str_or_null->type = QTYPE_QSTRING; + str_or_null->u.s = g_strdup(""); + } else { + /* the setter doesn't allow QNULL */ + assert(str_or_null->type != QTYPE_QNULL); + } + visit_type_str(v, name, &str_or_null->u.s, errp); +} + +static void set_StrOrNull(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + const Property *prop = opaque; + StrOrNull **ptr = object_field_prop_ptr(obj, prop); + StrOrNull *str_or_null = g_new0(StrOrNull, 1); + + /* + * Only str to keep compatibility, QNULL was never used via + * command line. + */ + str_or_null->type = QTYPE_QSTRING; + if (!visit_type_str(v, name, &str_or_null->u.s, errp)) { + return; + } + + qapi_free_StrOrNull(*ptr); + *ptr = str_or_null; +} + +static void release_StrOrNull(Object *obj, const char *name, void *opaque) +{ + const Property *prop = opaque; + qapi_free_StrOrNull(*(StrOrNull **)object_field_prop_ptr(obj, prop)); +} + +static void set_default_value_tls_opt(ObjectProperty *op, const Property *prop) +{ + object_property_set_default_str(op, ""); +} + +/* + * String property like qdev_prop_string, except it's backed by a + * StrOrNull instead of a char *. This is intended for + * TYPE_MIGRATION's TLS options. + */ +const PropertyInfo qdev_prop_StrOrNull = { + .type = "StrOrNull", + .get = get_StrOrNull, + .set = set_StrOrNull, + .release = release_StrOrNull, + .set_default_value = set_default_value_tls_opt, +}; + bool migrate_auto_converge(void) { MigrationState *s = migrate_get_current(); -- 2.50.1
From: Fabiano Rosas <farosas@suse.de> Make sure the TLS options handling is working correctly with a NULL parameter. This is relevant due to the usage of StrOrNull for the tls-creds, tls-authz and tls-hostname options. With this, all manners of passing TLS options are somehow covered by the tests, we should not need to do manual testing when touching TLS options code. Signed-off-by: Fabiano Rosas <farosas@suse.de> Reviewed-by: Peter Xu <peterx@redhat.com> Link: https://lore.kernel.org/r/20251215220041.12657-5-farosas@suse.de Signed-off-by: Peter Xu <peterx@redhat.com> --- tests/qtest/migration/migration-qmp.h | 1 + tests/qtest/migration/migration-qmp.c | 9 +++++ tests/qtest/migration/tls-tests.c | 56 +++++++++++++++++++++++++++ 3 files changed, 66 insertions(+) diff --git a/tests/qtest/migration/migration-qmp.h b/tests/qtest/migration/migration-qmp.h index XXXXXXX..XXXXXXX 100644 --- a/tests/qtest/migration/migration-qmp.h +++ b/tests/qtest/migration/migration-qmp.h @@ -XXX,XX +XXX,XX @@ void migrate_set_parameter_str(QTestState *who, const char *parameter, const char *value); void migrate_set_parameter_strv(QTestState *who, const char *parameter, char **strv); +void migrate_set_parameter_null(QTestState *who, const char *parameter); void migrate_set_parameter_bool(QTestState *who, const char *parameter, int value); void migrate_ensure_non_converge(QTestState *who); diff --git a/tests/qtest/migration/migration-qmp.c b/tests/qtest/migration/migration-qmp.c index XXXXXXX..XXXXXXX 100644 --- a/tests/qtest/migration/migration-qmp.c +++ b/tests/qtest/migration/migration-qmp.c @@ -XXX,XX +XXX,XX @@ void migrate_set_parameter_strv(QTestState *who, const char *parameter, qtest_qmp_assert_success(who, command, parameter); } +void migrate_set_parameter_null(QTestState *who, const char *parameter) +{ + qtest_qmp_assert_success(who, + "{ 'execute': 'migrate-set-parameters'," + "'arguments': { %s: null } }", + parameter); + migrate_check_parameter_str(who, parameter, ""); +} + static long long migrate_get_parameter_bool(QTestState *who, const char *parameter) { diff --git a/tests/qtest/migration/tls-tests.c b/tests/qtest/migration/tls-tests.c index XXXXXXX..XXXXXXX 100644 --- a/tests/qtest/migration/tls-tests.c +++ b/tests/qtest/migration/tls-tests.c @@ -XXX,XX +XXX,XX @@ static void test_precopy_tcp_tls_psk_mismatch(void) test_precopy_common(&args); } +static void *migrate_hook_start_no_tls(QTestState *from, QTestState *to) +{ + struct TestMigrateTLSPSKData *data = + g_new0(struct TestMigrateTLSPSKData, 1); + + migrate_set_parameter_null(from, "tls-creds"); + migrate_set_parameter_null(to, "tls-creds"); + + return data; +} + +static void test_precopy_tcp_no_tls(void) +{ + MigrateCommon args = { + .listen_uri = "tcp:127.0.0.1:0", + .start_hook = migrate_hook_start_no_tls, + .end_hook = migrate_hook_end_tls_psk, + }; + + test_precopy_common(&args); +} + +static void * +migrate_hook_start_tls_x509_no_host(QTestState *from, QTestState *to) +{ + TestMigrateTLSX509 args = { + .verifyclient = true, + .clientcert = true, + .authzclient = true, + }; + TestMigrateTLSX509Data *data = migrate_hook_start_tls_x509_common(from, to, + &args); + migrate_set_parameter_null(from, "tls-hostname"); + migrate_set_parameter_null(to, "tls-hostname"); + + return data; +} + +static void test_precopy_tcp_tls_no_hostname(void) +{ + MigrateCommon args = { + .listen_uri = "tcp:127.0.0.1:0", + .start_hook = migrate_hook_start_tls_x509_no_host, + .end_hook = migrate_hook_end_tls_x509, + .result = MIG_TEST_FAIL_DEST_QUIT_ERR, + .start.hide_stderr = true, + }; + + test_precopy_common(&args); +} + #ifdef CONFIG_TASN1 static void test_precopy_tcp_tls_x509_default_host(void) { @@ -XXX,XX +XXX,XX @@ void migration_test_add_tls(MigrationTestEnv *env) return; } + migration_test_add("/migration/precopy/tcp/no-tls", + test_precopy_tcp_no_tls); + migration_test_add("/migration/precopy/tcp/tls/no-hostname", + test_precopy_tcp_tls_no_hostname); + migration_test_add("/migration/precopy/unix/tls/psk", test_precopy_unix_tls_psk); -- 2.50.1
From: Fabiano Rosas <farosas@suse.de> The migration parameters tls_creds, tls_authz and tls_hostname currently have a non-uniform handling. When used as arguments to migrate-set-parameters, their type is StrOrNull and when used as return value from query-migrate-parameters their type is a plain string. Not only having to convert between the types is cumbersome, but it also creates the issue of requiring two different QAPI types to be used, one for each command. MigrateSetParameters is used for migrate-set-parameters with the TLS arguments as StrOrNull while MigrationParameters is used for query-migrate-parameters with the TLS arguments as str. Since StrOrNull could be considered a superset of str, change the type of the TLS arguments in MigrationParameters to StrOrNull. Also ensure that QTYPE_QNULL is never used. 1) migrate-set-parameters will always write QTYPE_QSTRING to s->parameters, either an empty or non-empty string. 2) query-migrate-parameters will always return a QTYPE_QSTRING, either empty or non-empty. 3) the migrate_tls_* helpers will always return a non-empty string or NULL, for the internal migration code's consumption. Points (1) and (2) above help simplify the parameters validation and the query command handling because s->parameters is already kept in the format that query-migrate-parameters (and info migrate_paramters) expect. Point (3) is so people don't need to care about StrOrNull in migration code. This will allow the type duplication to be removed in the next patches. Note that the type of @tls_creds, @tls-hostname, @tls-authz changes from str to StrOrNull in introspection of the query-migrate-parameters command. We accept this imprecision to enable de-duplication. There's no need to free the TLS options in migration_instance_finalize() because they're freed by the qdev properties .release method. Temporary in this patch: migrate_params_test_apply() copies s->parameters into a temporary structure, so it's necessary to drop the references to the TLS options if they were not set by the user to avoid double-free. This is fixed in the next patches. Acked-by: Markus Armbruster <armbru@redhat.com> Signed-off-by: Fabiano Rosas <farosas@suse.de> Link: https://lore.kernel.org/r/20251215220041.12657-6-farosas@suse.de [peterx: in hmp_info_migrate_parameters(), remove an extra dump of max_postcopy_bandwidth, introduced likely by accident] Signed-off-by: Peter Xu <peterx@redhat.com> --- qapi/migration.json | 6 +- migration/options.h | 1 + migration/migration-hmp-cmds.c | 6 +- migration/options.c | 144 +++++++++++++++++++-------------- migration/tls.c | 2 +- 5 files changed, 93 insertions(+), 66 deletions(-) diff --git a/qapi/migration.json b/qapi/migration.json index XXXXXXX..XXXXXXX 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -XXX,XX +XXX,XX @@ '*cpu-throttle-initial': 'uint8', '*cpu-throttle-increment': 'uint8', '*cpu-throttle-tailslow': 'bool', - '*tls-creds': 'str', - '*tls-hostname': 'str', - '*tls-authz': 'str', + '*tls-creds': 'StrOrNull', + '*tls-hostname': 'StrOrNull', + '*tls-authz': 'StrOrNull', '*max-bandwidth': 'size', '*avail-switchover-bandwidth': 'size', '*downtime-limit': 'uint64', diff --git a/migration/options.h b/migration/options.h index XXXXXXX..XXXXXXX 100644 --- a/migration/options.h +++ b/migration/options.h @@ -XXX,XX +XXX,XX @@ ZeroPageDetection migrate_zero_page_detection(void); bool migrate_params_check(MigrationParameters *params, Error **errp); void migrate_params_init(MigrationParameters *params); +void migrate_tls_opts_free(MigrationParameters *params); #endif diff --git a/migration/migration-hmp-cmds.c b/migration/migration-hmp-cmds.c index XXXXXXX..XXXXXXX 100644 --- a/migration/migration-hmp-cmds.c +++ b/migration/migration-hmp-cmds.c @@ -XXX,XX +XXX,XX @@ void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict) assert(params->tls_creds); monitor_printf(mon, "%s: '%s'\n", MigrationParameter_str(MIGRATION_PARAMETER_TLS_CREDS), - params->tls_creds); + params->tls_creds->u.s); assert(params->tls_hostname); monitor_printf(mon, "%s: '%s'\n", MigrationParameter_str(MIGRATION_PARAMETER_TLS_HOSTNAME), - params->tls_hostname); + params->tls_hostname->u.s); assert(params->tls_authz); monitor_printf(mon, "%s: '%s'\n", MigrationParameter_str(MIGRATION_PARAMETER_TLS_AUTHZ), - params->tls_authz); + params->tls_authz->u.s); assert(params->has_max_bandwidth); monitor_printf(mon, "%s: %" PRIu64 " bytes/second\n", MigrationParameter_str(MIGRATION_PARAMETER_MAX_BANDWIDTH), diff --git a/migration/options.c b/migration/options.c index XXXXXXX..XXXXXXX 100644 --- a/migration/options.c +++ b/migration/options.c @@ -XXX,XX +XXX,XX @@ const Property migration_properties[] = { DEFINE_PROP_SIZE("announce-step", MigrationState, parameters.announce_step, DEFAULT_MIGRATE_ANNOUNCE_STEP), - DEFINE_PROP_STRING("tls-creds", MigrationState, parameters.tls_creds), - DEFINE_PROP_STRING("tls-hostname", MigrationState, parameters.tls_hostname), - DEFINE_PROP_STRING("tls-authz", MigrationState, parameters.tls_authz), + DEFINE_PROP_STR_OR_NULL("tls-creds", MigrationState, parameters.tls_creds), + DEFINE_PROP_STR_OR_NULL("tls-hostname", MigrationState, + parameters.tls_hostname), + DEFINE_PROP_STR_OR_NULL("tls-authz", MigrationState, parameters.tls_authz), DEFINE_PROP_UINT64("x-vcpu-dirty-limit-period", MigrationState, parameters.x_vcpu_dirty_limit_period, DEFAULT_MIGRATE_VCPU_DIRTY_LIMIT_PERIOD), @@ -XXX,XX +XXX,XX @@ static void release_StrOrNull(Object *obj, const char *name, void *opaque) static void set_default_value_tls_opt(ObjectProperty *op, const Property *prop) { + /* + * Initialization to the empty string here is important so + * query-migrate-parameters doesn't need to deal with a NULL value + * when it's called before any TLS option has been set. + */ object_property_set_default_str(op, ""); } @@ -XXX,XX +XXX,XX @@ bool migrate_rdma(void) return s->rdma_migration; } -bool migrate_tls(void) -{ - MigrationState *s = migrate_get_current(); - - return s->parameters.tls_creds && *s->parameters.tls_creds; -} - typedef enum WriteTrackingSupport { WT_SUPPORT_UNKNOWN = 0, WT_SUPPORT_ABSENT, @@ -XXX,XX +XXX,XX @@ const char *migrate_tls_authz(void) { MigrationState *s = migrate_get_current(); - return s->parameters.tls_authz; + if (*s->parameters.tls_authz->u.s) { + return s->parameters.tls_authz->u.s; + } + + return NULL; } const char *migrate_tls_creds(void) { MigrationState *s = migrate_get_current(); - return s->parameters.tls_creds; + if (*s->parameters.tls_creds->u.s) { + return s->parameters.tls_creds->u.s; + } + + return NULL; } const char *migrate_tls_hostname(void) { MigrationState *s = migrate_get_current(); - return s->parameters.tls_hostname; + if (*s->parameters.tls_hostname->u.s) { + return s->parameters.tls_hostname->u.s; + } + + return NULL; +} + +bool migrate_tls(void) +{ + return !!migrate_tls_creds(); } uint64_t migrate_vcpu_dirty_limit_period(void) @@ -XXX,XX +XXX,XX @@ AnnounceParameters *migrate_announce_params(void) return ≈ } +void migrate_tls_opts_free(MigrationParameters *params) +{ + qapi_free_StrOrNull(params->tls_creds); + qapi_free_StrOrNull(params->tls_hostname); + qapi_free_StrOrNull(params->tls_authz); +} + +/* normalize QTYPE_QNULL to QTYPE_QSTRING "" */ +static void tls_opt_to_str(StrOrNull *opt) +{ + if (!opt || opt->type == QTYPE_QSTRING) { + return; + } + + qobject_unref(opt->u.n); + opt->type = QTYPE_QSTRING; + opt->u.s = g_strdup(""); +} + MigrationParameters *qmp_query_migrate_parameters(Error **errp) { MigrationParameters *params; @@ -XXX,XX +XXX,XX @@ MigrationParameters *qmp_query_migrate_parameters(Error **errp) params->cpu_throttle_increment = s->parameters.cpu_throttle_increment; params->has_cpu_throttle_tailslow = true; params->cpu_throttle_tailslow = s->parameters.cpu_throttle_tailslow; - params->tls_creds = g_strdup(s->parameters.tls_creds); - params->tls_hostname = g_strdup(s->parameters.tls_hostname); - params->tls_authz = g_strdup(s->parameters.tls_authz ? - s->parameters.tls_authz : ""); + params->tls_creds = QAPI_CLONE(StrOrNull, s->parameters.tls_creds); + params->tls_hostname = QAPI_CLONE(StrOrNull, s->parameters.tls_hostname); + params->tls_authz = QAPI_CLONE(StrOrNull, s->parameters.tls_authz); params->has_max_bandwidth = true; params->max_bandwidth = s->parameters.max_bandwidth; params->has_avail_switchover_bandwidth = true; @@ -XXX,XX +XXX,XX @@ MigrationParameters *qmp_query_migrate_parameters(Error **errp) void migrate_params_init(MigrationParameters *params) { - params->tls_hostname = g_strdup(""); - params->tls_creds = g_strdup(""); - /* Set has_* up only for parameter checks */ params->has_throttle_trigger_threshold = true; params->has_cpu_throttle_initial = true; @@ -XXX,XX +XXX,XX @@ bool migrate_params_check(MigrationParameters *params, Error **errp) #ifdef CONFIG_LINUX if (migrate_zero_copy_send() && ((params->has_multifd_compression && params->multifd_compression) || - (params->tls_creds && *params->tls_creds))) { + *params->tls_creds->u.s)) { error_setg(errp, "Zero copy only available for non-compressed non-TLS multifd migration"); return false; @@ -XXX,XX +XXX,XX @@ static void migrate_params_test_apply(MigrateSetParameters *params, } if (params->tls_creds) { - assert(params->tls_creds->type == QTYPE_QSTRING); - dest->tls_creds = params->tls_creds->u.s; + dest->tls_creds = QAPI_CLONE(StrOrNull, params->tls_creds); + } else { + /* clear the reference, it's owned by s->parameters */ + dest->tls_creds = NULL; } if (params->tls_hostname) { - assert(params->tls_hostname->type == QTYPE_QSTRING); - dest->tls_hostname = params->tls_hostname->u.s; + dest->tls_hostname = QAPI_CLONE(StrOrNull, params->tls_hostname); + } else { + /* clear the reference, it's owned by s->parameters */ + dest->tls_hostname = NULL; } if (params->tls_authz) { - assert(params->tls_authz->type == QTYPE_QSTRING); - dest->tls_authz = params->tls_authz->u.s; + dest->tls_authz = QAPI_CLONE(StrOrNull, params->tls_authz); + } else { + /* clear the reference, it's owned by s->parameters */ + dest->tls_authz = NULL; } if (params->has_max_bandwidth) { @@ -XXX,XX +XXX,XX @@ static void migrate_params_apply(MigrateSetParameters *params, Error **errp) } if (params->tls_creds) { - g_free(s->parameters.tls_creds); - assert(params->tls_creds->type == QTYPE_QSTRING); - s->parameters.tls_creds = g_strdup(params->tls_creds->u.s); + qapi_free_StrOrNull(s->parameters.tls_creds); + s->parameters.tls_creds = QAPI_CLONE(StrOrNull, params->tls_creds); } if (params->tls_hostname) { - g_free(s->parameters.tls_hostname); - assert(params->tls_hostname->type == QTYPE_QSTRING); - s->parameters.tls_hostname = g_strdup(params->tls_hostname->u.s); + qapi_free_StrOrNull(s->parameters.tls_hostname); + s->parameters.tls_hostname = QAPI_CLONE(StrOrNull, + params->tls_hostname); } if (params->tls_authz) { - g_free(s->parameters.tls_authz); - assert(params->tls_authz->type == QTYPE_QSTRING); - s->parameters.tls_authz = g_strdup(params->tls_authz->u.s); + qapi_free_StrOrNull(s->parameters.tls_authz); + s->parameters.tls_authz = QAPI_CLONE(StrOrNull, params->tls_authz); } if (params->has_max_bandwidth) { @@ -XXX,XX +XXX,XX @@ void qmp_migrate_set_parameters(MigrateSetParameters *params, Error **errp) { MigrationParameters tmp; - /* TODO Rewrite "" to null instead for all three tls_* parameters */ - if (params->tls_creds - && params->tls_creds->type == QTYPE_QNULL) { - qobject_unref(params->tls_creds->u.n); - params->tls_creds->type = QTYPE_QSTRING; - params->tls_creds->u.s = strdup(""); - } - if (params->tls_hostname - && params->tls_hostname->type == QTYPE_QNULL) { - qobject_unref(params->tls_hostname->u.n); - params->tls_hostname->type = QTYPE_QSTRING; - params->tls_hostname->u.s = strdup(""); - } - if (params->tls_authz - && params->tls_authz->type == QTYPE_QNULL) { - qobject_unref(params->tls_authz->u.n); - params->tls_authz->type = QTYPE_QSTRING; - params->tls_authz->u.s = strdup(""); - } + /* + * Convert QTYPE_QNULL and NULL to the empty string (""). Even + * though NULL is cleaner to deal with in C code, that would force + * query-migrate-parameters to convert it once more to the empty + * string, so avoid that. The migrate_tls_*() helpers that expose + * the options to the rest of the migration code already use + * return NULL when the empty string is found. + */ + tls_opt_to_str(params->tls_creds); + tls_opt_to_str(params->tls_hostname); + tls_opt_to_str(params->tls_authz); migrate_params_test_apply(params, &tmp); - if (!migrate_params_check(&tmp, errp)) { - /* Invalid parameter */ - return; + if (migrate_params_check(&tmp, errp)) { + migrate_params_apply(params, errp); } - migrate_params_apply(params, errp); + migrate_tls_opts_free(&tmp); } diff --git a/migration/tls.c b/migration/tls.c index XXXXXXX..XXXXXXX 100644 --- a/migration/tls.c +++ b/migration/tls.c @@ -XXX,XX +XXX,XX @@ QIOChannelTLS *migration_tls_client_create(QIOChannel *ioc, } const char *tls_hostname = migrate_tls_hostname(); - if (tls_hostname && *tls_hostname) { + if (tls_hostname) { hostname = tls_hostname; } -- 2.50.1
From: Fabiano Rosas <farosas@suse.de> Now that the TLS options have been made the same between migrate-set-parameters and query-migrate-parameters, a single type can be used. Remove MigrateSetParameters. The TLS options documentation from MigrationParameters were replaced with the ones from MigrateSetParameters which was more complete. Acked-by: Markus Armbruster <armbru@redhat.com> Acked-by: Peter Xu <peterx@redhat.com> Signed-off-by: Fabiano Rosas <farosas@suse.de> Link: https://lore.kernel.org/r/20251215220041.12657-7-farosas@suse.de Signed-off-by: Peter Xu <peterx@redhat.com> --- qapi/migration.json | 239 +++------------------------------ migration/migration-hmp-cmds.c | 4 +- migration/options.c | 6 +- 3 files changed, 26 insertions(+), 223 deletions(-) diff --git a/qapi/migration.json b/qapi/migration.json index XXXXXXX..XXXXXXX 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -XXX,XX +XXX,XX @@ 'cpr-exec-command'] } ## -# @MigrateSetParameters: +# @migrate-set-parameters: +# +# Set migration parameters. All arguments are optional. +# +# Since: 2.4 +# +# .. qmp-example:: +# +# -> { "execute": "migrate-set-parameters" , +# "arguments": { "multifd-channels": 5 } } +# <- { "return": {} } +## +{ 'command': 'migrate-set-parameters', 'boxed': true, + 'data': 'MigrationParameters' } + +## +# @MigrationParameters: # # @announce-initial: Initial delay (in milliseconds) before sending # the first announce (Since 4.0) @@ -XXX,XX +XXX,XX @@ # @unstable: Members @x-checkpoint-delay and # @x-vcpu-dirty-limit-period are experimental. # -# TODO: either fuse back into `MigrationParameters`, or make -# `MigrationParameters` members mandatory -# -# Since: 2.4 -## -{ 'struct': 'MigrateSetParameters', - 'data': { '*announce-initial': 'size', - '*announce-max': 'size', - '*announce-rounds': 'size', - '*announce-step': 'size', - '*throttle-trigger-threshold': 'uint8', - '*cpu-throttle-initial': 'uint8', - '*cpu-throttle-increment': 'uint8', - '*cpu-throttle-tailslow': 'bool', - '*tls-creds': 'StrOrNull', - '*tls-hostname': 'StrOrNull', - '*tls-authz': 'StrOrNull', - '*max-bandwidth': 'size', - '*avail-switchover-bandwidth': 'size', - '*downtime-limit': 'uint64', - '*x-checkpoint-delay': { 'type': 'uint32', - 'features': [ 'unstable' ] }, - '*multifd-channels': 'uint8', - '*xbzrle-cache-size': 'size', - '*max-postcopy-bandwidth': 'size', - '*max-cpu-throttle': 'uint8', - '*multifd-compression': 'MultiFDCompression', - '*multifd-zlib-level': 'uint8', - '*multifd-qatzip-level': 'uint8', - '*multifd-zstd-level': 'uint8', - '*block-bitmap-mapping': [ 'BitmapMigrationNodeAlias' ], - '*x-vcpu-dirty-limit-period': { 'type': 'uint64', - 'features': [ 'unstable' ] }, - '*vcpu-dirty-limit': 'uint64', - '*mode': 'MigMode', - '*zero-page-detection': 'ZeroPageDetection', - '*direct-io': 'bool', - '*cpr-exec-command': [ 'str' ]} } - -## -# @migrate-set-parameters: -# -# Set various migration parameters. -# -# Since: 2.4 -# -# .. qmp-example:: -# -# -> { "execute": "migrate-set-parameters" , -# "arguments": { "multifd-channels": 5 } } -# <- { "return": {} } -## -{ 'command': 'migrate-set-parameters', 'boxed': true, - 'data': 'MigrateSetParameters' } - -## -# @MigrationParameters: -# -# The optional members aren't actually optional. -# -# @announce-initial: Initial delay (in milliseconds) before sending -# the first announce (Since 4.0) -# -# @announce-max: Maximum delay (in milliseconds) between packets in -# the announcement (Since 4.0) -# -# @announce-rounds: Number of self-announce packets sent after -# migration (Since 4.0) -# -# @announce-step: Increase in delay (in milliseconds) between -# subsequent packets in the announcement (Since 4.0) -# -# @throttle-trigger-threshold: The ratio of bytes_dirty_period and -# bytes_xfer_period to trigger throttling. It is expressed as -# percentage. The default value is 50. (Since 5.0) -# -# @cpu-throttle-initial: Initial percentage of time guest cpus are -# throttled when migration auto-converge is activated. -# (Since 2.7) -# -# @cpu-throttle-increment: throttle percentage increase each time -# auto-converge detects that migration is not making progress. -# (Since 2.7) -# -# @cpu-throttle-tailslow: Make CPU throttling slower at tail stage. -# At the tail stage of throttling, the Guest is very sensitive to -# CPU percentage while the @cpu-throttle -increment is excessive -# usually at tail stage. If this parameter is true, we will -# compute the ideal CPU percentage used by the Guest, which may -# exactly make the dirty rate match the dirty rate threshold. -# Then we will choose a smaller throttle increment between the one -# specified by @cpu-throttle-increment and the one generated by -# ideal CPU percentage. Therefore, it is compatible to -# traditional throttling, meanwhile the throttle increment won't -# be excessive at tail stage. The default value is false. -# (Since 5.1) -# -# @tls-creds: ID of the 'tls-creds' object that provides credentials -# for establishing a TLS connection over the migration data -# channel. On the outgoing side of the migration, the credentials -# must be for a 'client' endpoint, while for the incoming side the -# credentials must be for a 'server' endpoint. An empty string -# means that QEMU will use plain text mode for migration, rather -# than TLS. (Since 2.7) -# -# Note: 2.8 omits empty @tls-creds instead. -# -# @tls-hostname: migration target's hostname for validating the -# server's x509 certificate identity. If empty, QEMU will use the -# hostname from the migration URI, if any. (Since 2.7) -# -# Note: 2.8 omits empty @tls-hostname instead. -# -# @tls-authz: ID of the 'authz' object subclass that provides access -# control checking of the TLS x509 certificate distinguished name. -# (Since 4.0) -# -# @max-bandwidth: maximum speed for migration, in bytes per second. -# (Since 2.8) -# -# @avail-switchover-bandwidth: to set the available bandwidth that -# migration can use during switchover phase. **Note:** this does -# not limit the bandwidth during switchover, but only for -# calculations when making decisions to switchover. By default, -# this value is zero, which means QEMU will estimate the bandwidth -# automatically. This can be set when the estimated value is not -# accurate, while the user is able to guarantee such bandwidth is -# available when switching over. When specified correctly, this -# can make the switchover decision much more accurate. -# (Since 8.2) -# -# @downtime-limit: set maximum tolerated downtime for migration. -# maximum downtime in milliseconds (Since 2.8) -# -# @x-checkpoint-delay: the delay time between two COLO checkpoints. -# (Since 2.8) -# -# @multifd-channels: Number of channels used to migrate data in -# parallel. This is the same number that the number of sockets -# used for migration. The default value is 2 (since 4.0) -# -# @xbzrle-cache-size: cache size to be used by XBZRLE migration. It -# needs to be a multiple of the target page size and a power of 2 -# (Since 2.11) -# -# @max-postcopy-bandwidth: Background transfer bandwidth during -# postcopy. Defaults to 0 (unlimited). In bytes per second. -# (Since 3.0) -# -# @max-cpu-throttle: maximum cpu throttle percentage. Defaults to 99. -# (Since 3.1) -# -# @multifd-compression: Which compression method to use. Defaults to -# none. (Since 5.0) -# -# @multifd-zlib-level: Set the compression level to be used in live -# migration, the compression level is an integer between 0 and 9, -# where 0 means no compression, 1 means the best compression -# speed, and 9 means best compression ratio which will consume -# more CPU. Defaults to 1. (Since 5.0) -# -# @multifd-qatzip-level: Set the compression level to be used in live -# migration. The level is an integer between 1 and 9, where 1 -# means the best compression speed, and 9 means the best -# compression ratio which will consume more CPU. Defaults to 1. -# (Since 9.2) -# -# @multifd-zstd-level: Set the compression level to be used in live -# migration, the compression level is an integer between 0 and 20, -# where 0 means no compression, 1 means the best compression -# speed, and 20 means best compression ratio which will consume -# more CPU. Defaults to 1. (Since 5.0) -# -# @block-bitmap-mapping: Maps block nodes and bitmaps on them to -# aliases for the purpose of dirty bitmap migration. Such aliases -# may for example be the corresponding names on the opposite site. -# The mapping must be one-to-one, but not necessarily complete: On -# the source, unmapped bitmaps and all bitmaps on unmapped nodes -# will be ignored. On the destination, encountering an unmapped -# alias in the incoming migration stream will result in a report, -# and all further bitmap migration data will then be discarded. -# Note that the destination does not know about bitmaps it does -# not receive, so there is no limitation or requirement regarding -# the number of bitmaps received, or how they are named, or on -# which nodes they are placed. By default (when this parameter -# has never been set), bitmap names are mapped to themselves. -# Nodes are mapped to their block device name if there is one, and -# to their node name otherwise. (Since 5.2) -# -# @x-vcpu-dirty-limit-period: Periodic time (in milliseconds) of dirty -# limit during live migration. Should be in the range 1 to -# 1000ms. Defaults to 1000ms. (Since 8.1) -# -# @vcpu-dirty-limit: Dirtyrate limit (MB/s) during live migration. -# Defaults to 1. (Since 8.1) -# -# @mode: Migration mode. See description in `MigMode`. Default is -# 'normal'. (Since 8.2) -# -# @zero-page-detection: Whether and how to detect zero pages. See -# description in `ZeroPageDetection`. Default is 'multifd'. -# (since 9.0) -# -# @direct-io: Open migration files with O_DIRECT when possible. This -# only has effect if the @mapped-ram capability is enabled. -# (Since 9.1) -# -# @cpr-exec-command: Command to start the new QEMU process when @mode -# is @cpr-exec. The first list element is the program's filename, -# the remainder its arguments. (Since 10.2) -# -# Features: -# -# @unstable: Members @x-checkpoint-delay and -# @x-vcpu-dirty-limit-period are experimental. -# # Since: 2.4 ## { 'struct': 'MigrationParameters', @@ -XXX,XX +XXX,XX @@ ## # @query-migrate-parameters: # -# Return information about the current migration parameters +# Return information about the current migration parameters. Optional +# members of the return value are always present, except +# @block-bitmap-mapping, which is only present if it has been +# previously set. # # Since: 2.4 # diff --git a/migration/migration-hmp-cmds.c b/migration/migration-hmp-cmds.c index XXXXXXX..XXXXXXX 100644 --- a/migration/migration-hmp-cmds.c +++ b/migration/migration-hmp-cmds.c @@ -XXX,XX +XXX,XX @@ void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict) const char *param = qdict_get_str(qdict, "parameter"); const char *valuestr = qdict_get_str(qdict, "value"); Visitor *v = string_input_visitor_new(valuestr); - MigrateSetParameters *p = g_new0(MigrateSetParameters, 1); + MigrationParameters *p = g_new0(MigrationParameters, 1); uint64_t valuebw = 0; uint64_t cache_size; Error *err = NULL; @@ -XXX,XX +XXX,XX @@ void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict) qmp_migrate_set_parameters(p, &err); cleanup: - qapi_free_MigrateSetParameters(p); + qapi_free_MigrationParameters(p); visit_free(v); hmp_handle_error(mon, err); } diff --git a/migration/options.c b/migration/options.c index XXXXXXX..XXXXXXX 100644 --- a/migration/options.c +++ b/migration/options.c @@ -XXX,XX +XXX,XX @@ bool migrate_params_check(MigrationParameters *params, Error **errp) return true; } -static void migrate_params_test_apply(MigrateSetParameters *params, +static void migrate_params_test_apply(MigrationParameters *params, MigrationParameters *dest) { *dest = migrate_get_current()->parameters; @@ -XXX,XX +XXX,XX @@ static void migrate_params_test_apply(MigrateSetParameters *params, } } -static void migrate_params_apply(MigrateSetParameters *params, Error **errp) +static void migrate_params_apply(MigrationParameters *params, Error **errp) { MigrationState *s = migrate_get_current(); @@ -XXX,XX +XXX,XX @@ static void migrate_params_apply(MigrateSetParameters *params, Error **errp) } } -void qmp_migrate_set_parameters(MigrateSetParameters *params, Error **errp) +void qmp_migrate_set_parameters(MigrationParameters *params, Error **errp) { MigrationParameters tmp; -- 2.50.1
From: Fabiano Rosas <farosas@suse.de> The MigrationParameter (singular) enumeration is not part of the migration QMP API, it's only used for nicely converting HMP strings into MigrationParameters (plural) members and for providing readline completion. Documenting this enum only serves to duplicate documentation between MigrationParameter and MigrationParameters. Add an exception to QAPIs pragma.json and stop documenting it. The generated "QEMU QMP Reference Manual" now lists the enum members as "Not documented." Tolerable. Acked-by: Markus Armbruster <armbru@redhat.com> Acked-by: Peter Xu <peterx@redhat.com> Signed-off-by: Fabiano Rosas <farosas@suse.de> Link: https://lore.kernel.org/r/20251215220041.12657-8-farosas@suse.de Signed-off-by: Peter Xu <peterx@redhat.com> --- qapi/migration.json | 154 +------------------------------------------- qapi/pragma.json | 1 + 2 files changed, 3 insertions(+), 152 deletions(-) diff --git a/qapi/migration.json b/qapi/migration.json index XXXXXXX..XXXXXXX 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -XXX,XX +XXX,XX @@ ## # @MigrationParameter: # -# Migration parameters enumeration -# -# @announce-initial: Initial delay (in milliseconds) before sending -# the first announce (Since 4.0) -# -# @announce-max: Maximum delay (in milliseconds) between packets in -# the announcement (Since 4.0) -# -# @announce-rounds: Number of self-announce packets sent after -# migration (Since 4.0) -# -# @announce-step: Increase in delay (in milliseconds) between -# subsequent packets in the announcement (Since 4.0) -# -# @throttle-trigger-threshold: The ratio of bytes_dirty_period and -# bytes_xfer_period to trigger throttling. It is expressed as -# percentage. The default value is 50. (Since 5.0) -# -# @cpu-throttle-initial: Initial percentage of time guest cpus are -# throttled when migration auto-converge is activated. The -# default value is 20. (Since 2.7) -# -# @cpu-throttle-increment: throttle percentage increase each time -# auto-converge detects that migration is not making progress. -# The default value is 10. (Since 2.7) -# -# @cpu-throttle-tailslow: Make CPU throttling slower at tail stage. -# At the tail stage of throttling, the Guest is very sensitive to -# CPU percentage while the @cpu-throttle -increment is excessive -# usually at tail stage. If this parameter is true, we will -# compute the ideal CPU percentage used by the Guest, which may -# exactly make the dirty rate match the dirty rate threshold. -# Then we will choose a smaller throttle increment between the one -# specified by @cpu-throttle-increment and the one generated by -# ideal CPU percentage. Therefore, it is compatible to -# traditional throttling, meanwhile the throttle increment won't -# be excessive at tail stage. The default value is false. -# (Since 5.1) -# -# @tls-creds: ID of the 'tls-creds' object that provides credentials -# for establishing a TLS connection over the migration data -# channel. On the outgoing side of the migration, the credentials -# must be for a 'client' endpoint, while for the incoming side the -# credentials must be for a 'server' endpoint. Setting this to a -# non-empty string enables TLS for all migrations. An empty -# string means that QEMU will use plain text mode for migration, -# rather than TLS. (Since 2.7) -# -# @tls-hostname: migration target's hostname for validating the -# server's x509 certificate identity. If empty, QEMU will use the -# hostname from the migration URI, if any. A non-empty value is -# required when using x509 based TLS credentials and the migration -# URI does not include a hostname, such as fd: or exec: based -# migration. (Since 2.7) -# -# Note: empty value works only since 2.9. -# -# @tls-authz: ID of the 'authz' object subclass that provides access -# control checking of the TLS x509 certificate distinguished name. -# This object is only resolved at time of use, so can be deleted -# and recreated on the fly while the migration server is active. -# If missing, it will default to denying access (Since 4.0) -# -# @max-bandwidth: maximum speed for migration, in bytes per second. -# (Since 2.8) -# -# @avail-switchover-bandwidth: to set the available bandwidth that -# migration can use during switchover phase. **Note:** this does -# not limit the bandwidth during switchover, but only for -# calculations when making decisions to switchover. By default, -# this value is zero, which means QEMU will estimate the bandwidth -# automatically. This can be set when the estimated value is not -# accurate, while the user is able to guarantee such bandwidth is -# available when switching over. When specified correctly, this -# can make the switchover decision much more accurate. -# (Since 8.2) -# -# @downtime-limit: set maximum tolerated downtime for migration. -# maximum downtime in milliseconds (Since 2.8) -# -# @x-checkpoint-delay: The delay time (in ms) between two COLO -# checkpoints in periodic mode. (Since 2.8) -# -# @multifd-channels: Number of channels used to migrate data in -# parallel. This is the same number that the number of sockets -# used for migration. The default value is 2 (since 4.0) -# -# @xbzrle-cache-size: cache size to be used by XBZRLE migration. It -# needs to be a multiple of the target page size and a power of 2 -# (Since 2.11) -# -# @max-postcopy-bandwidth: Background transfer bandwidth during -# postcopy. Defaults to 0 (unlimited). In bytes per second. -# (Since 3.0) -# -# @max-cpu-throttle: maximum cpu throttle percentage. Defaults to 99. -# (Since 3.1) -# -# @multifd-compression: Which compression method to use. Defaults to -# none. (Since 5.0) -# -# @multifd-zlib-level: Set the compression level to be used in live -# migration, the compression level is an integer between 0 and 9, -# where 0 means no compression, 1 means the best compression -# speed, and 9 means best compression ratio which will consume -# more CPU. Defaults to 1. (Since 5.0) -# -# @multifd-qatzip-level: Set the compression level to be used in live -# migration. The level is an integer between 1 and 9, where 1 -# means the best compression speed, and 9 means the best -# compression ratio which will consume more CPU. Defaults to 1. -# (Since 9.2) -# -# @multifd-zstd-level: Set the compression level to be used in live -# migration, the compression level is an integer between 0 and 20, -# where 0 means no compression, 1 means the best compression -# speed, and 20 means best compression ratio which will consume -# more CPU. Defaults to 1. (Since 5.0) -# -# @block-bitmap-mapping: Maps block nodes and bitmaps on them to -# aliases for the purpose of dirty bitmap migration. Such aliases -# may for example be the corresponding names on the opposite site. -# The mapping must be one-to-one, but not necessarily complete: On -# the source, unmapped bitmaps and all bitmaps on unmapped nodes -# will be ignored. On the destination, encountering an unmapped -# alias in the incoming migration stream will result in a report, -# and all further bitmap migration data will then be discarded. -# Note that the destination does not know about bitmaps it does -# not receive, so there is no limitation or requirement regarding -# the number of bitmaps received, or how they are named, or on -# which nodes they are placed. By default (when this parameter -# has never been set), bitmap names are mapped to themselves. -# Nodes are mapped to their block device name if there is one, and -# to their node name otherwise. (Since 5.2) -# -# @x-vcpu-dirty-limit-period: Periodic time (in milliseconds) of dirty -# limit during live migration. Should be in the range 1 to -# 1000ms. Defaults to 1000ms. (Since 8.1) -# -# @vcpu-dirty-limit: Dirtyrate limit (MB/s) during live migration. -# Defaults to 1. (Since 8.1) -# -# @mode: Migration mode. See description in `MigMode`. Default is -# 'normal'. (Since 8.2) -# -# @zero-page-detection: Whether and how to detect zero pages. See -# description in `ZeroPageDetection`. Default is 'multifd'. -# (since 9.0) -# -# @direct-io: Open migration files with O_DIRECT when possible. This -# only has effect if the @mapped-ram capability is enabled. -# (Since 9.1) +# Migration parameters enumeration. The enumeration values mirror the +# members of @MigrationParameters. # # @cpr-exec-command: Command to start the new QEMU process when @mode # is @cpr-exec. The first list element is the program's filename, diff --git a/qapi/pragma.json b/qapi/pragma.json index XXXXXXX..XXXXXXX 100644 --- a/qapi/pragma.json +++ b/qapi/pragma.json @@ -XXX,XX +XXX,XX @@ 'IscsiTransport', 'KeyValueKind', 'MemoryDeviceInfoKind', + 'MigrationParameter', 'NetClientDriver', 'ObjectType', 'QKeyCode', -- 2.50.1
From: Fabiano Rosas <farosas@suse.de> Some migration parameters are updated immediately once they are set via migrate-set-parameters. Move that work outside of migrate_params_apply() and leave that function with the single responsibility of setting s->parameters and not doing any side-effects. Reviewed-by: Peter Xu <peterx@redhat.com> Signed-off-by: Fabiano Rosas <farosas@suse.de> Link: https://lore.kernel.org/r/20251215220041.12657-9-farosas@suse.de Signed-off-by: Peter Xu <peterx@redhat.com> --- migration/options.c | 38 ++++++++++++++++++++++++++++---------- migration/ram.c | 2 +- 2 files changed, 29 insertions(+), 11 deletions(-) diff --git a/migration/options.c b/migration/options.c index XXXXXXX..XXXXXXX 100644 --- a/migration/options.c +++ b/migration/options.c @@ -XXX,XX +XXX,XX @@ void migrate_params_init(MigrationParameters *params) params->has_cpr_exec_command = true; } +static void migrate_post_update_params(MigrationParameters *new, Error **errp) +{ + MigrationState *s = migrate_get_current(); + + if (new->has_max_bandwidth) { + if (s->to_dst_file && !migration_in_postcopy()) { + migration_rate_set(new->max_bandwidth); + } + } + + if (new->has_x_checkpoint_delay) { + colo_checkpoint_delay_set(); + } + + if (new->has_xbzrle_cache_size) { + xbzrle_cache_resize(new->xbzrle_cache_size, errp); + } + + if (new->has_max_postcopy_bandwidth) { + if (s->to_dst_file && migration_in_postcopy()) { + migration_rate_set(new->max_postcopy_bandwidth); + } + } +} + /* * Check whether the parameters are valid. Error will be put into errp * (if provided). Return true if valid, otherwise false. @@ -XXX,XX +XXX,XX @@ static void migrate_params_test_apply(MigrationParameters *params, } } -static void migrate_params_apply(MigrationParameters *params, Error **errp) +static void migrate_params_apply(MigrationParameters *params) { MigrationState *s = migrate_get_current(); @@ -XXX,XX +XXX,XX @@ static void migrate_params_apply(MigrationParameters *params, Error **errp) if (params->has_max_bandwidth) { s->parameters.max_bandwidth = params->max_bandwidth; - if (s->to_dst_file && !migration_in_postcopy()) { - migration_rate_set(s->parameters.max_bandwidth); - } } if (params->has_avail_switchover_bandwidth) { @@ -XXX,XX +XXX,XX @@ static void migrate_params_apply(MigrationParameters *params, Error **errp) if (params->has_x_checkpoint_delay) { s->parameters.x_checkpoint_delay = params->x_checkpoint_delay; - colo_checkpoint_delay_set(); } if (params->has_multifd_channels) { @@ -XXX,XX +XXX,XX @@ static void migrate_params_apply(MigrationParameters *params, Error **errp) } if (params->has_xbzrle_cache_size) { s->parameters.xbzrle_cache_size = params->xbzrle_cache_size; - xbzrle_cache_resize(params->xbzrle_cache_size, errp); } if (params->has_max_postcopy_bandwidth) { s->parameters.max_postcopy_bandwidth = params->max_postcopy_bandwidth; - if (s->to_dst_file && migration_in_postcopy()) { - migration_rate_set(s->parameters.max_postcopy_bandwidth); - } } if (params->has_max_cpu_throttle) { s->parameters.max_cpu_throttle = params->max_cpu_throttle; @@ -XXX,XX +XXX,XX @@ void qmp_migrate_set_parameters(MigrationParameters *params, Error **errp) migrate_params_test_apply(params, &tmp); if (migrate_params_check(&tmp, errp)) { - migrate_params_apply(params, errp); + migrate_params_apply(params); + migrate_post_update_params(params, errp); } migrate_tls_opts_free(&tmp); diff --git a/migration/ram.c b/migration/ram.c index XXXXXXX..XXXXXXX 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -XXX,XX +XXX,XX @@ static void XBZRLE_cache_unlock(void) /** * xbzrle_cache_resize: resize the xbzrle cache * - * This function is called from migrate_params_apply in main + * This function is called from migrate_post_update_params in main * thread, possibly while a migration is in progress. A running * migration may be using the cache and might finish during this call, * hence changes to the cache are protected by XBZRLE.lock(). -- 2.50.1
From: Fabiano Rosas <farosas@suse.de> The QAPI converts an empty list on the block-bitmap-mapping input into a NULL BitmapMigrationNodeAliasList. The empty list is a valid input for the block-bitmap-mapping option, so commit 3cba22c9ad ("migration: Fix block_bitmap_mapping migration") started using the s->parameters.has_block_bitmap_mapping field to tell when the user has passed in an empty list vs. when no list has been passed at all. Using s->parameters.has_block_bitmap_mapping field is only possible because MigrationParameters has had its members made optional due to historical reasons. In order to make improvements to the way configuration options are set for a migration, we'd like to reduce the open-coded usage of the has_* fields of the global configuration object (s->parameters). Add a separate boolean to track the status of the block_bitmap_mapping option. No functional change intended. (this was verified to not regress iotest 300, which is the test that 3cba22c9ad refers to) CC: Kevin Wolf <kwolf@redhat.com> Acked-by: Peter Xu <peterx@redhat.com> Signed-off-by: Fabiano Rosas <farosas@suse.de> Link: https://lore.kernel.org/r/20251215220041.12657-10-farosas@suse.de Signed-off-by: Peter Xu <peterx@redhat.com> --- migration/migration.h | 7 +++++++ migration/migration-hmp-cmds.c | 3 ++- migration/options.c | 6 +++--- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/migration/migration.h b/migration/migration.h index XXXXXXX..XXXXXXX 100644 --- a/migration/migration.h +++ b/migration/migration.h @@ -XXX,XX +XXX,XX @@ struct MigrationState { QemuEvent postcopy_package_loaded_event; GSource *hup_source; + + /* + * The block-bitmap-mapping option is allowed to be an empty list, + * therefore we need a way to know whether the user has given + * anything as input. + */ + bool has_block_bitmap_mapping; }; void migrate_set_state(MigrationStatus *state, MigrationStatus old_state, diff --git a/migration/migration-hmp-cmds.c b/migration/migration-hmp-cmds.c index XXXXXXX..XXXXXXX 100644 --- a/migration/migration-hmp-cmds.c +++ b/migration/migration-hmp-cmds.c @@ -XXX,XX +XXX,XX @@ static void monitor_print_cpr_exec_command(Monitor *mon, strList *args) void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict) { MigrationParameters *params; + MigrationState *s = migrate_get_current(); params = qmp_query_migrate_parameters(NULL); @@ -XXX,XX +XXX,XX @@ void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict) MigrationParameter_str(MIGRATION_PARAMETER_XBZRLE_CACHE_SIZE), params->xbzrle_cache_size); - if (params->has_block_bitmap_mapping) { + if (s->has_block_bitmap_mapping) { const BitmapMigrationNodeAliasList *bmnal; monitor_printf(mon, "%s:\n", diff --git a/migration/options.c b/migration/options.c index XXXXXXX..XXXXXXX 100644 --- a/migration/options.c +++ b/migration/options.c @@ -XXX,XX +XXX,XX @@ bool migrate_has_block_bitmap_mapping(void) { MigrationState *s = migrate_get_current(); - return s->parameters.has_block_bitmap_mapping; + return s->has_block_bitmap_mapping; } uint32_t migrate_checkpoint_delay(void) @@ -XXX,XX +XXX,XX @@ MigrationParameters *qmp_query_migrate_parameters(Error **errp) params->has_announce_step = true; params->announce_step = s->parameters.announce_step; - if (s->parameters.has_block_bitmap_mapping) { + if (s->has_block_bitmap_mapping) { params->has_block_bitmap_mapping = true; params->block_bitmap_mapping = QAPI_CLONE(BitmapMigrationNodeAliasList, @@ -XXX,XX +XXX,XX @@ static void migrate_params_apply(MigrationParameters *params) qapi_free_BitmapMigrationNodeAliasList( s->parameters.block_bitmap_mapping); - s->parameters.has_block_bitmap_mapping = true; + s->has_block_bitmap_mapping = true; s->parameters.block_bitmap_mapping = QAPI_CLONE(BitmapMigrationNodeAliasList, params->block_bitmap_mapping); -- 2.50.1
From: Fabiano Rosas <farosas@suse.de> The migration parameters validation produces a temporary structure which is the merge of the current parameter values (s->parameters, MigrationParameters) with the new parameters set by the user (former MigrateSetParameters). When copying the values from s->parameters into the temporary structure, the has_* fields are copied along, but when merging the user-input values they are not. During migrate_params_check(), only the parameters that have the corresponding has_* field will be checked, so only the parameters that were initialized in migrate_params_init() will be validated. This causes (almost) all of the migration parameters to be validated every time a parameter is set, regardless of which fields the user touched, but it also skips validation of any values that are not set in migrate_params_init(). It's not clear what was the intention of the original code, whether to validate all fields always, or only validate what the user input changed. Since the current situation is closer to the former option, make the choice of validating all parameters by removing the checks for the has_* fields when validating. Note that bringing the user input into the temporary structure for validation still needs to look at the has_* fields, otherwise any parameters not set by the user (i.e. 0) would override the corresponding value in s->parameters. The empty migrate_params_init() will be kept because subsequent patches will add code to it. Signed-off-by: Fabiano Rosas <farosas@suse.de> Link: https://lore.kernel.org/r/20251215220041.12657-11-farosas@suse.de Signed-off-by: Peter Xu <peterx@redhat.com> --- migration/migration.c | 2 +- migration/options.c | 102 ++++++++++++------------------------------ 2 files changed, 30 insertions(+), 74 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index XXXXXXX..XXXXXXX 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -XXX,XX +XXX,XX @@ static bool migrate_prepare(MigrationState *s, bool resume, Error **errp) } if (migrate_mode() == MIG_MODE_CPR_EXEC && - !s->parameters.has_cpr_exec_command) { + !s->parameters.cpr_exec_command) { error_setg(errp, "cpr-exec mode requires setting cpr-exec-command"); return false; } diff --git a/migration/options.c b/migration/options.c index XXXXXXX..XXXXXXX 100644 --- a/migration/options.c +++ b/migration/options.c @@ -XXX,XX +XXX,XX @@ MigrationParameters *qmp_query_migrate_parameters(Error **errp) void migrate_params_init(MigrationParameters *params) { - /* Set has_* up only for parameter checks */ - params->has_throttle_trigger_threshold = true; - params->has_cpu_throttle_initial = true; - params->has_cpu_throttle_increment = true; - params->has_cpu_throttle_tailslow = true; - params->has_max_bandwidth = true; - params->has_downtime_limit = true; - params->has_x_checkpoint_delay = true; - params->has_multifd_channels = true; - params->has_multifd_compression = true; - params->has_multifd_zlib_level = true; - params->has_multifd_qatzip_level = true; - params->has_multifd_zstd_level = true; - params->has_xbzrle_cache_size = true; - params->has_max_postcopy_bandwidth = true; - params->has_max_cpu_throttle = true; - params->has_announce_initial = true; - params->has_announce_max = true; - params->has_announce_rounds = true; - params->has_announce_step = true; - params->has_x_vcpu_dirty_limit_period = true; - params->has_vcpu_dirty_limit = true; - params->has_mode = true; - params->has_zero_page_detection = true; - params->has_direct_io = true; - params->has_cpr_exec_command = true; } static void migrate_post_update_params(MigrationParameters *new, Error **errp) @@ -XXX,XX +XXX,XX @@ bool migrate_params_check(MigrationParameters *params, Error **errp) { ERRP_GUARD(); - if (params->has_throttle_trigger_threshold && - (params->throttle_trigger_threshold < 1 || - params->throttle_trigger_threshold > 100)) { + if (params->throttle_trigger_threshold < 1 || + params->throttle_trigger_threshold > 100) { error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "throttle_trigger_threshold", "an integer in the range of 1 to 100"); return false; } - if (params->has_cpu_throttle_initial && - (params->cpu_throttle_initial < 1 || - params->cpu_throttle_initial > 99)) { + if (params->cpu_throttle_initial < 1 || + params->cpu_throttle_initial > 99) { error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "cpu_throttle_initial", "an integer in the range of 1 to 99"); return false; } - if (params->has_cpu_throttle_increment && - (params->cpu_throttle_increment < 1 || - params->cpu_throttle_increment > 99)) { + if (params->cpu_throttle_increment < 1 || + params->cpu_throttle_increment > 99) { error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "cpu_throttle_increment", "an integer in the range of 1 to 99"); return false; } - if (params->has_max_bandwidth && (params->max_bandwidth > SIZE_MAX)) { + if (params->max_bandwidth > SIZE_MAX) { error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "max_bandwidth", "an integer in the range of 0 to "stringify(SIZE_MAX) @@ -XXX,XX +XXX,XX @@ bool migrate_params_check(MigrationParameters *params, Error **errp) return false; } - if (params->has_avail_switchover_bandwidth && - (params->avail_switchover_bandwidth > SIZE_MAX)) { + if (params->avail_switchover_bandwidth > SIZE_MAX) { error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "avail_switchover_bandwidth", "an integer in the range of 0 to "stringify(SIZE_MAX) @@ -XXX,XX +XXX,XX @@ bool migrate_params_check(MigrationParameters *params, Error **errp) return false; } - if (params->has_downtime_limit && - (params->downtime_limit > MAX_MIGRATE_DOWNTIME)) { + if (params->downtime_limit > MAX_MIGRATE_DOWNTIME) { error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "downtime_limit", "an integer in the range of 0 to " @@ -XXX,XX +XXX,XX @@ bool migrate_params_check(MigrationParameters *params, Error **errp) /* x_checkpoint_delay is now always positive */ - if (params->has_multifd_channels && (params->multifd_channels < 1)) { + if (params->multifd_channels < 1) { error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "multifd_channels", "a value between 1 and 255"); return false; } - if (params->has_multifd_zlib_level && - (params->multifd_zlib_level > 9)) { + if (params->multifd_zlib_level > 9) { error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "multifd_zlib_level", "a value between 0 and 9"); return false; } - if (params->has_multifd_qatzip_level && - ((params->multifd_qatzip_level > 9) || - (params->multifd_qatzip_level < 1))) { + if (params->multifd_qatzip_level > 9 || + params->multifd_qatzip_level < 1) { error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "multifd_qatzip_level", "a value between 1 and 9"); return false; } - if (params->has_multifd_zstd_level && - (params->multifd_zstd_level > 20)) { + if (params->multifd_zstd_level > 20) { error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "multifd_zstd_level", "a value between 0 and 20"); return false; } - if (params->has_xbzrle_cache_size && - (params->xbzrle_cache_size < qemu_target_page_size() || - !is_power_of_2(params->xbzrle_cache_size))) { + if (params->xbzrle_cache_size < qemu_target_page_size() || + !is_power_of_2(params->xbzrle_cache_size)) { error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "xbzrle_cache_size", "a power of two no less than the target page size"); return false; } - if (params->has_max_cpu_throttle && - (params->max_cpu_throttle < params->cpu_throttle_initial || - params->max_cpu_throttle > 99)) { + if (params->max_cpu_throttle < params->cpu_throttle_initial || + params->max_cpu_throttle > 99) { error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "max_cpu_throttle", "an integer in the range of cpu_throttle_initial to 99"); return false; } - if (params->has_announce_initial && - params->announce_initial > 100000) { + if (params->announce_initial > 100000) { error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "announce_initial", "a value between 0 and 100000"); return false; } - if (params->has_announce_max && - params->announce_max > 100000) { + if (params->announce_max > 100000) { error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "announce_max", "a value between 0 and 100000"); return false; } - if (params->has_announce_rounds && - params->announce_rounds > 1000) { + if (params->announce_rounds > 1000) { error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "announce_rounds", "a value between 0 and 1000"); return false; } - if (params->has_announce_step && - (params->announce_step < 1 || - params->announce_step > 10000)) { + if (params->announce_step < 1 || + params->announce_step > 10000) { error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "announce_step", "a value between 0 and 10000"); return false; } - if (params->has_block_bitmap_mapping && - !check_dirty_bitmap_mig_alias_map(params->block_bitmap_mapping, errp)) { + if (!check_dirty_bitmap_mig_alias_map(params->block_bitmap_mapping, errp)) { error_prepend(errp, "Invalid mapping given for block-bitmap-mapping: "); return false; } #ifdef CONFIG_LINUX if (migrate_zero_copy_send() && - ((params->has_multifd_compression && params->multifd_compression) || - *params->tls_creds->u.s)) { + (params->multifd_compression || *params->tls_creds->u.s)) { error_setg(errp, "Zero copy only available for non-compressed non-TLS multifd migration"); return false; @@ -XXX,XX +XXX,XX @@ bool migrate_params_check(MigrationParameters *params, Error **errp) return false; } - if (params->has_x_vcpu_dirty_limit_period && - (params->x_vcpu_dirty_limit_period < 1 || - params->x_vcpu_dirty_limit_period > 1000)) { + if (params->x_vcpu_dirty_limit_period < 1 || + params->x_vcpu_dirty_limit_period > 1000) { error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "x-vcpu-dirty-limit-period", "a value between 1 and 1000"); return false; } - if (params->has_vcpu_dirty_limit && - (params->vcpu_dirty_limit < 1)) { + if (params->vcpu_dirty_limit < 1) { error_setg(errp, "Parameter 'vcpu_dirty_limit' must be greater than 1 MB/s"); return false; } - if (params->has_direct_io && params->direct_io && !qemu_has_direct_io()) { + if (params->direct_io && !qemu_has_direct_io()) { error_setg(errp, "No build-time support for direct-io"); return false; } -- 2.50.1
From: Fabiano Rosas <farosas@suse.de> The QERR_INVALID_PARAMETER_VALUE macro is documented as not to be used in new code. Remove the usage from migration/options.c. Reviewed-by: Peter Xu <peterx@redhat.com> Signed-off-by: Fabiano Rosas <farosas@suse.de> Link: https://lore.kernel.org/r/20251215220041.12657-12-farosas@suse.de Signed-off-by: Peter Xu <peterx@redhat.com> --- migration/migration.c | 3 +-- migration/options.c | 56 +++++++++++++++--------------------------- migration/page_cache.c | 6 ++--- migration/ram.c | 3 +-- 4 files changed, 24 insertions(+), 44 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index XXXXXXX..XXXXXXX 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -XXX,XX +XXX,XX @@ static void qmp_migrate_finish(MigrationAddress *addr, bool resume_requested, } else if (addr->transport == MIGRATION_ADDRESS_TYPE_FILE) { file_start_outgoing_migration(s, &addr->u.file, &local_err); } else { - error_setg(&local_err, QERR_INVALID_PARAMETER_VALUE, "uri", - "a valid migration protocol"); + error_setg(&local_err, "uri is not a valid migration protocol"); migrate_set_state(&s->state, MIGRATION_STATUS_SETUP, MIGRATION_STATUS_FAILED); } diff --git a/migration/options.c b/migration/options.c index XXXXXXX..XXXXXXX 100644 --- a/migration/options.c +++ b/migration/options.c @@ -XXX,XX +XXX,XX @@ bool migrate_params_check(MigrationParameters *params, Error **errp) if (params->throttle_trigger_threshold < 1 || params->throttle_trigger_threshold > 100) { - error_setg(errp, QERR_INVALID_PARAMETER_VALUE, - "throttle_trigger_threshold", + error_setg(errp, "Option throttle_trigger_threshold expects " "an integer in the range of 1 to 100"); return false; } if (params->cpu_throttle_initial < 1 || params->cpu_throttle_initial > 99) { - error_setg(errp, QERR_INVALID_PARAMETER_VALUE, - "cpu_throttle_initial", + error_setg(errp, "Option cpu_throttle_initial expects " "an integer in the range of 1 to 99"); return false; } if (params->cpu_throttle_increment < 1 || params->cpu_throttle_increment > 99) { - error_setg(errp, QERR_INVALID_PARAMETER_VALUE, - "cpu_throttle_increment", + error_setg(errp, "Option cpu_throttle_increment expects " "an integer in the range of 1 to 99"); return false; } if (params->max_bandwidth > SIZE_MAX) { - error_setg(errp, QERR_INVALID_PARAMETER_VALUE, - "max_bandwidth", + error_setg(errp, "Option max_bandwidth expects " "an integer in the range of 0 to "stringify(SIZE_MAX) " bytes/second"); return false; } if (params->avail_switchover_bandwidth > SIZE_MAX) { - error_setg(errp, QERR_INVALID_PARAMETER_VALUE, - "avail_switchover_bandwidth", + error_setg(errp, "Option avail_switchover_bandwidth expects " "an integer in the range of 0 to "stringify(SIZE_MAX) " bytes/second"); return false; } if (params->downtime_limit > MAX_MIGRATE_DOWNTIME) { - error_setg(errp, QERR_INVALID_PARAMETER_VALUE, - "downtime_limit", + error_setg(errp, "Option downtime_limit expects " "an integer in the range of 0 to " stringify(MAX_MIGRATE_DOWNTIME)" ms"); return false; } - /* x_checkpoint_delay is now always positive */ - if (params->multifd_channels < 1) { - error_setg(errp, QERR_INVALID_PARAMETER_VALUE, - "multifd_channels", + error_setg(errp, "Option multifd_channels expects " "a value between 1 and 255"); return false; } if (params->multifd_zlib_level > 9) { - error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "multifd_zlib_level", + error_setg(errp, "Option multifd_zlib_level expects " "a value between 0 and 9"); return false; } if (params->multifd_qatzip_level > 9 || params->multifd_qatzip_level < 1) { - error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "multifd_qatzip_level", + error_setg(errp, "Option multifd_qatzip_level expects " "a value between 1 and 9"); return false; } if (params->multifd_zstd_level > 20) { - error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "multifd_zstd_level", + error_setg(errp, "Option multifd_zstd_level expects " "a value between 0 and 20"); return false; } if (params->xbzrle_cache_size < qemu_target_page_size() || !is_power_of_2(params->xbzrle_cache_size)) { - error_setg(errp, QERR_INVALID_PARAMETER_VALUE, - "xbzrle_cache_size", + error_setg(errp, "Option xbzrle_cache_size expects " "a power of two no less than the target page size"); return false; } if (params->max_cpu_throttle < params->cpu_throttle_initial || params->max_cpu_throttle > 99) { - error_setg(errp, QERR_INVALID_PARAMETER_VALUE, - "max_cpu_throttle", + error_setg(errp, "max_Option cpu_throttle expects " "an integer in the range of cpu_throttle_initial to 99"); return false; } if (params->announce_initial > 100000) { - error_setg(errp, QERR_INVALID_PARAMETER_VALUE, - "announce_initial", + error_setg(errp, "Option announce_initial expects " "a value between 0 and 100000"); return false; } if (params->announce_max > 100000) { - error_setg(errp, QERR_INVALID_PARAMETER_VALUE, - "announce_max", + error_setg(errp, "Option announce_max expects " "a value between 0 and 100000"); - return false; + return false; } if (params->announce_rounds > 1000) { - error_setg(errp, QERR_INVALID_PARAMETER_VALUE, - "announce_rounds", + error_setg(errp, "Option announce_rounds expects " "a value between 0 and 1000"); - return false; + return false; } if (params->announce_step < 1 || params->announce_step > 10000) { - error_setg(errp, QERR_INVALID_PARAMETER_VALUE, - "announce_step", + error_setg(errp, "Option announce_step expects " "a value between 0 and 10000"); - return false; + return false; } if (!check_dirty_bitmap_mig_alias_map(params->block_bitmap_mapping, errp)) { @@ -XXX,XX +XXX,XX @@ bool migrate_params_check(MigrationParameters *params, Error **errp) if (params->x_vcpu_dirty_limit_period < 1 || params->x_vcpu_dirty_limit_period > 1000) { - error_setg(errp, QERR_INVALID_PARAMETER_VALUE, - "x-vcpu-dirty-limit-period", + error_setg(errp, "Option x-vcpu-dirty-limit-period expects " "a value between 1 and 1000"); return false; } diff --git a/migration/page_cache.c b/migration/page_cache.c index XXXXXXX..XXXXXXX 100644 --- a/migration/page_cache.c +++ b/migration/page_cache.c @@ -XXX,XX +XXX,XX @@ PageCache *cache_init(uint64_t new_size, size_t page_size, Error **errp) PageCache *cache; if (new_size < page_size) { - error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "cache size", - "is smaller than one target page size"); + error_setg(errp, "cache size is smaller than target page size"); return NULL; } /* round down to the nearest power of 2 */ if (!is_power_of_2(num_pages)) { - error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "cache size", - "is not a power of two number of pages"); + error_setg(errp, "number of pages is not a power of two"); return NULL; } diff --git a/migration/ram.c b/migration/ram.c index XXXXXXX..XXXXXXX 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -XXX,XX +XXX,XX @@ int xbzrle_cache_resize(uint64_t new_size, Error **errp) /* Check for truncation */ if (new_size != (size_t)new_size) { - error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "cache size", - "exceeding address space"); + error_setg(errp, "xbzrle cache size integer overflow"); return -1; } -- 2.50.1
From: Fabiano Rosas <farosas@suse.de> MigrationParameters needs to have all of its has_* fields marked as true when used as the return of query_migrate_parameters because the corresponding QMP command has all of its members non-optional by design, despite them being marked as optional in migration.json. Extract this code into a function and make it assert if any field is missing. With this we ensure future changes will not inadvertently leave any parameters missing. Note that the block-bitmap-mapping is a special case because the empty list is considered a valid value, so it has historically not been present in the command's output if it has never been set. CC: Kevin Wolf <kwolf@redhat.com> Signed-off-by: Fabiano Rosas <farosas@suse.de> Link: https://lore.kernel.org/r/20251215220041.12657-13-farosas@suse.de Signed-off-by: Peter Xu <peterx@redhat.com> --- migration/options.c | 89 ++++++++++++++++++++++++++++----------------- 1 file changed, 55 insertions(+), 34 deletions(-) diff --git a/migration/options.c b/migration/options.c index XXXXXXX..XXXXXXX 100644 --- a/migration/options.c +++ b/migration/options.c @@ -XXX,XX +XXX,XX @@ static void tls_opt_to_str(StrOrNull *opt) opt->u.s = g_strdup(""); } +/* + * query-migrate-parameters expects all members of MigrationParameters + * to be present, but we cannot mark them non-optional in QAPI because + * the structure is also used for migrate-set-parameters, which needs + * the optionality. Force all parameters to be seen as present + * now. Note that this depends on some form of default being set for + * every member of MigrationParameters, currently done during qdev + * init using migration_properties defined in this file. The TLS + * options are a special case because they don't have a default and + * need to be normalized before use. + */ +static void migrate_mark_all_params_present(MigrationParameters *p) +{ + int len, n_str_args = 3; /* tls-creds, tls-hostname, tls-authz */ + bool *has_fields[] = { + &p->has_throttle_trigger_threshold, &p->has_cpu_throttle_initial, + &p->has_cpu_throttle_increment, &p->has_cpu_throttle_tailslow, + &p->has_max_bandwidth, &p->has_avail_switchover_bandwidth, + &p->has_downtime_limit, &p->has_x_checkpoint_delay, + &p->has_multifd_channels, &p->has_multifd_compression, + &p->has_multifd_zlib_level, &p->has_multifd_qatzip_level, + &p->has_multifd_zstd_level, &p->has_xbzrle_cache_size, + &p->has_max_postcopy_bandwidth, &p->has_max_cpu_throttle, + &p->has_announce_initial, &p->has_announce_max, &p->has_announce_rounds, + &p->has_announce_step, &p->has_block_bitmap_mapping, + &p->has_x_vcpu_dirty_limit_period, &p->has_vcpu_dirty_limit, + &p->has_mode, &p->has_zero_page_detection, &p->has_direct_io, + &p->has_cpr_exec_command, + }; + + len = ARRAY_SIZE(has_fields); + assert(len + n_str_args == MIGRATION_PARAMETER__MAX); + + for (int i = 0; i < len; i++) { + *has_fields[i] = true; + } +} + MigrationParameters *qmp_query_migrate_parameters(Error **errp) { MigrationParameters *params; @@ -XXX,XX +XXX,XX @@ MigrationParameters *qmp_query_migrate_parameters(Error **errp) /* TODO use QAPI_CLONE() instead of duplicating it inline */ params = g_malloc0(sizeof(*params)); - params->has_throttle_trigger_threshold = true; + params->throttle_trigger_threshold = s->parameters.throttle_trigger_threshold; - params->has_cpu_throttle_initial = true; params->cpu_throttle_initial = s->parameters.cpu_throttle_initial; - params->has_cpu_throttle_increment = true; params->cpu_throttle_increment = s->parameters.cpu_throttle_increment; - params->has_cpu_throttle_tailslow = true; params->cpu_throttle_tailslow = s->parameters.cpu_throttle_tailslow; params->tls_creds = QAPI_CLONE(StrOrNull, s->parameters.tls_creds); params->tls_hostname = QAPI_CLONE(StrOrNull, s->parameters.tls_hostname); params->tls_authz = QAPI_CLONE(StrOrNull, s->parameters.tls_authz); - params->has_max_bandwidth = true; params->max_bandwidth = s->parameters.max_bandwidth; - params->has_avail_switchover_bandwidth = true; params->avail_switchover_bandwidth = s->parameters.avail_switchover_bandwidth; - params->has_downtime_limit = true; params->downtime_limit = s->parameters.downtime_limit; - params->has_x_checkpoint_delay = true; params->x_checkpoint_delay = s->parameters.x_checkpoint_delay; - params->has_multifd_channels = true; params->multifd_channels = s->parameters.multifd_channels; - params->has_multifd_compression = true; params->multifd_compression = s->parameters.multifd_compression; - params->has_multifd_zlib_level = true; params->multifd_zlib_level = s->parameters.multifd_zlib_level; - params->has_multifd_qatzip_level = true; params->multifd_qatzip_level = s->parameters.multifd_qatzip_level; - params->has_multifd_zstd_level = true; params->multifd_zstd_level = s->parameters.multifd_zstd_level; - params->has_xbzrle_cache_size = true; params->xbzrle_cache_size = s->parameters.xbzrle_cache_size; - params->has_max_postcopy_bandwidth = true; params->max_postcopy_bandwidth = s->parameters.max_postcopy_bandwidth; - params->has_max_cpu_throttle = true; params->max_cpu_throttle = s->parameters.max_cpu_throttle; - params->has_announce_initial = true; params->announce_initial = s->parameters.announce_initial; - params->has_announce_max = true; params->announce_max = s->parameters.announce_max; - params->has_announce_rounds = true; params->announce_rounds = s->parameters.announce_rounds; - params->has_announce_step = true; params->announce_step = s->parameters.announce_step; - - if (s->has_block_bitmap_mapping) { - params->has_block_bitmap_mapping = true; - params->block_bitmap_mapping = - QAPI_CLONE(BitmapMigrationNodeAliasList, - s->parameters.block_bitmap_mapping); - } - - params->has_x_vcpu_dirty_limit_period = true; params->x_vcpu_dirty_limit_period = s->parameters.x_vcpu_dirty_limit_period; - params->has_vcpu_dirty_limit = true; params->vcpu_dirty_limit = s->parameters.vcpu_dirty_limit; - params->has_mode = true; params->mode = s->parameters.mode; - params->has_zero_page_detection = true; params->zero_page_detection = s->parameters.zero_page_detection; - params->has_direct_io = true; params->direct_io = s->parameters.direct_io; - params->has_cpr_exec_command = true; params->cpr_exec_command = QAPI_CLONE(strList, s->parameters.cpr_exec_command); + params->block_bitmap_mapping = + QAPI_CLONE(BitmapMigrationNodeAliasList, + s->parameters.block_bitmap_mapping); + + migrate_mark_all_params_present(params); + + /* + * The block-bitmap-mapping breaks the expected API of + * query-migrate-parameters of having all members present. To keep + * compatibility, only emit this field if it's actually been + * set. The empty list is a valid value. + */ + if (!s->has_block_bitmap_mapping) { + params->has_block_bitmap_mapping = false; + qapi_free_BitmapMigrationNodeAliasList(params->block_bitmap_mapping); + } return params; } -- 2.50.1
From: Fabiano Rosas <farosas@suse.de> QAPI_CLONE_MEMBERS is a better option than copying parameters one by one because it operates on the entire struct and follows pointers. It also avoids the need to alter this function every time a new parameter is added. For this to work, the has_* fields of s->parameters need to be already set beforehand, so move migrate_mark_all_params_present() to the init routine. Reviewed-by: Peter Xu <peterx@redhat.com> Signed-off-by: Fabiano Rosas <farosas@suse.de> Link: https://lore.kernel.org/r/20251215220041.12657-14-farosas@suse.de Signed-off-by: Peter Xu <peterx@redhat.com> --- migration/options.c | 43 +++---------------------------------------- 1 file changed, 3 insertions(+), 40 deletions(-) diff --git a/migration/options.c b/migration/options.c index XXXXXXX..XXXXXXX 100644 --- a/migration/options.c +++ b/migration/options.c @@ -XXX,XX +XXX,XX @@ static void migrate_mark_all_params_present(MigrationParameters *p) MigrationParameters *qmp_query_migrate_parameters(Error **errp) { - MigrationParameters *params; MigrationState *s = migrate_get_current(); - - /* TODO use QAPI_CLONE() instead of duplicating it inline */ - params = g_malloc0(sizeof(*params)); - - params->throttle_trigger_threshold = s->parameters.throttle_trigger_threshold; - params->cpu_throttle_initial = s->parameters.cpu_throttle_initial; - params->cpu_throttle_increment = s->parameters.cpu_throttle_increment; - params->cpu_throttle_tailslow = s->parameters.cpu_throttle_tailslow; - params->tls_creds = QAPI_CLONE(StrOrNull, s->parameters.tls_creds); - params->tls_hostname = QAPI_CLONE(StrOrNull, s->parameters.tls_hostname); - params->tls_authz = QAPI_CLONE(StrOrNull, s->parameters.tls_authz); - params->max_bandwidth = s->parameters.max_bandwidth; - params->avail_switchover_bandwidth = s->parameters.avail_switchover_bandwidth; - params->downtime_limit = s->parameters.downtime_limit; - params->x_checkpoint_delay = s->parameters.x_checkpoint_delay; - params->multifd_channels = s->parameters.multifd_channels; - params->multifd_compression = s->parameters.multifd_compression; - params->multifd_zlib_level = s->parameters.multifd_zlib_level; - params->multifd_qatzip_level = s->parameters.multifd_qatzip_level; - params->multifd_zstd_level = s->parameters.multifd_zstd_level; - params->xbzrle_cache_size = s->parameters.xbzrle_cache_size; - params->max_postcopy_bandwidth = s->parameters.max_postcopy_bandwidth; - params->max_cpu_throttle = s->parameters.max_cpu_throttle; - params->announce_initial = s->parameters.announce_initial; - params->announce_max = s->parameters.announce_max; - params->announce_rounds = s->parameters.announce_rounds; - params->announce_step = s->parameters.announce_step; - params->x_vcpu_dirty_limit_period = s->parameters.x_vcpu_dirty_limit_period; - params->vcpu_dirty_limit = s->parameters.vcpu_dirty_limit; - params->mode = s->parameters.mode; - params->zero_page_detection = s->parameters.zero_page_detection; - params->direct_io = s->parameters.direct_io; - params->cpr_exec_command = QAPI_CLONE(strList, - s->parameters.cpr_exec_command); - params->block_bitmap_mapping = - QAPI_CLONE(BitmapMigrationNodeAliasList, - s->parameters.block_bitmap_mapping); - - migrate_mark_all_params_present(params); + MigrationParameters *params = QAPI_CLONE(MigrationParameters, + &s->parameters); /* * The block-bitmap-mapping breaks the expected API of @@ -XXX,XX +XXX,XX @@ MigrationParameters *qmp_query_migrate_parameters(Error **errp) void migrate_params_init(MigrationParameters *params) { + migrate_mark_all_params_present(params); } static void migrate_post_update_params(MigrationParameters *new, Error **errp) -- 2.50.1
From: Fabiano Rosas <farosas@suse.de> With the upcoming addition of the config QDict, the tests will need a better way of managing the memory of the test data than putting the test arguments on the stack of the test functions. The config QDict will need to be merged into the arguments of migrate_qmp* functions, which causes a refcount increment, so the test functions would need to allocate and deref the config QDict themselves. A better approach is to already pass the arguments into the test functions and do the memory management in the existing wrapper. There is already migration_test_destroy(), which is called for every test. Do the following: - merge the two existing wrappers, migration_test_wrapper() and migration_test_wrapper_full(). The latter was pioneer in passing data into the tests, but now all tests will receive data, so we don't need it anymore. The usage of migration_test_wrapper_full() was in passing a slightly different test name string into the cancel tests, so still keep the migration_test_add_suffix() function. - add (char *name, MigrateCommon *args) to the signature of all test functions. - alter any code to stop allocating args on the stack and instead use the object that came as parameter. - pass args around as needed. - while here, order args (MigrateCommon) before args->start (MigrateStart) and put a blank like in between. No functional change. Signed-off-by: Fabiano Rosas <farosas@suse.de> Link: https://lore.kernel.org/r/20251215220041.12657-26-farosas@suse.de [peterx: fix a conflict with newly added mapped-ram+ignore-share test] Signed-off-by: Peter Xu <peterx@redhat.com> --- tests/qtest/migration/migration-util.h | 8 +- tests/qtest/migration/compression-tests.c | 131 +++--- tests/qtest/migration/cpr-tests.c | 75 ++-- tests/qtest/migration/file-tests.c | 206 +++++----- tests/qtest/migration/migration-util.c | 26 +- tests/qtest/migration/misc-tests.c | 112 +++-- tests/qtest/migration/postcopy-tests.c | 82 ++-- tests/qtest/migration/precopy-tests.c | 348 +++++++--------- tests/qtest/migration/tls-tests.c | 477 ++++++++++------------ 9 files changed, 652 insertions(+), 813 deletions(-) diff --git a/tests/qtest/migration/migration-util.h b/tests/qtest/migration/migration-util.h index XXXXXXX..XXXXXXX 100644 --- a/tests/qtest/migration/migration-util.h +++ b/tests/qtest/migration/migration-util.h @@ -XXX,XX +XXX,XX @@ #include "libqtest.h" +#include "migration/framework.h" + typedef struct QTestMigrationState { bool stop_seen; bool resume_seen; @@ -XXX,XX +XXX,XX @@ static inline bool probe_o_direct_support(const char *tmpfs) bool ufd_version_check(bool *uffd_feature_thread_id); bool kvm_dirty_ring_supported(void); -void migration_test_add(const char *path, void (*fn)(void)); + +void migration_test_add(const char *path, + void (*fn)(char *name, MigrateCommon *args)); void migration_test_add_suffix(const char *path, const char *suffix, - void (*fn)(void *)); + void (*fn)(char *name, MigrateCommon *args)); char *migrate_get_connect_uri(QTestState *who); void migrate_set_ports(QTestState *to, QList *channel_list); diff --git a/tests/qtest/migration/compression-tests.c b/tests/qtest/migration/compression-tests.c index XXXXXXX..XXXXXXX 100644 --- a/tests/qtest/migration/compression-tests.c +++ b/tests/qtest/migration/compression-tests.c @@ -XXX,XX +XXX,XX @@ migrate_hook_start_precopy_tcp_multifd_zstd(QTestState *from, return migrate_hook_start_precopy_tcp_multifd_common(from, to, "zstd"); } -static void test_multifd_tcp_zstd(void) +static void test_multifd_tcp_zstd(char *name, MigrateCommon *args) { - MigrateCommon args = { - .listen_uri = "defer", - .start = { - .caps[MIGRATION_CAPABILITY_MULTIFD] = true, - }, - .start_hook = migrate_hook_start_precopy_tcp_multifd_zstd, - }; - test_precopy_common(&args); + args->listen_uri = "defer"; + args->start_hook = migrate_hook_start_precopy_tcp_multifd_zstd; + + args->start.caps[MIGRATION_CAPABILITY_MULTIFD] = true; + + test_precopy_common(args); } -static void test_multifd_postcopy_tcp_zstd(void) +static void test_multifd_postcopy_tcp_zstd(char *name, MigrateCommon *args) { - 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); + args->listen_uri = "defer"; + args->start_hook = migrate_hook_start_precopy_tcp_multifd_zstd, + + args->start.caps[MIGRATION_CAPABILITY_MULTIFD] = true; + args->start.caps[MIGRATION_CAPABILITY_POSTCOPY_RAM] = true; + + test_precopy_common(args); } #endif /* CONFIG_ZSTD */ @@ -XXX,XX +XXX,XX @@ migrate_hook_start_precopy_tcp_multifd_qatzip(QTestState *from, return migrate_hook_start_precopy_tcp_multifd_common(from, to, "qatzip"); } -static void test_multifd_tcp_qatzip(void) +static void test_multifd_tcp_qatzip(char *name, MigrateCommon *args) { - MigrateCommon args = { - .listen_uri = "defer", - .start = { - .caps[MIGRATION_CAPABILITY_MULTIFD] = true, - }, - .start_hook = migrate_hook_start_precopy_tcp_multifd_qatzip, - }; - test_precopy_common(&args); + args->listen_uri = "defer"; + args->start_hook = migrate_hook_start_precopy_tcp_multifd_qatzip; + + args->start.caps[MIGRATION_CAPABILITY_MULTIFD] = true; + + test_precopy_common(args); } #endif @@ -XXX,XX +XXX,XX @@ migrate_hook_start_precopy_tcp_multifd_qpl(QTestState *from, return migrate_hook_start_precopy_tcp_multifd_common(from, to, "qpl"); } -static void test_multifd_tcp_qpl(void) +static void test_multifd_tcp_qpl(char *name, MigrateCommon *args) { - MigrateCommon args = { - .listen_uri = "defer", - .start = { - .caps[MIGRATION_CAPABILITY_MULTIFD] = true, - }, - .start_hook = migrate_hook_start_precopy_tcp_multifd_qpl, - }; - test_precopy_common(&args); + args->listen_uri = "defer"; + args->start_hook = migrate_hook_start_precopy_tcp_multifd_qpl; + + args->start.caps[MIGRATION_CAPABILITY_MULTIFD] = true; + + test_precopy_common(args); } #endif /* CONFIG_QPL */ @@ -XXX,XX +XXX,XX @@ migrate_hook_start_precopy_tcp_multifd_uadk(QTestState *from, return migrate_hook_start_precopy_tcp_multifd_common(from, to, "uadk"); } -static void test_multifd_tcp_uadk(void) +static void test_multifd_tcp_uadk(char *name, MigrateCommon *args) { - MigrateCommon args = { - .listen_uri = "defer", - .start = { - .caps[MIGRATION_CAPABILITY_MULTIFD] = true, - }, - .start_hook = migrate_hook_start_precopy_tcp_multifd_uadk, - }; - test_precopy_common(&args); + args->listen_uri = "defer"; + args->start_hook = migrate_hook_start_precopy_tcp_multifd_uadk; + + args->start.caps[MIGRATION_CAPABILITY_MULTIFD] = true; + + test_precopy_common(args); } #endif /* CONFIG_UADK */ @@ -XXX,XX +XXX,XX @@ migrate_hook_start_xbzrle(QTestState *from, return NULL; } -static void test_precopy_unix_xbzrle(void) +static void test_precopy_unix_xbzrle(char *name, MigrateCommon *args) { g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); - MigrateCommon args = { - .connect_uri = uri, - .listen_uri = uri, - .start_hook = migrate_hook_start_xbzrle, - .iterations = 2, - .start = { - .caps[MIGRATION_CAPABILITY_XBZRLE] = true, - }, - /* - * XBZRLE needs pages to be modified when doing the 2nd+ round - * iteration to have real data pushed to the stream. - */ - .live = true, - }; - - test_precopy_common(&args); + + args->connect_uri = uri; + args->listen_uri = uri; + args->start_hook = migrate_hook_start_xbzrle; + args->iterations = 2; + /* + * XBZRLE needs pages to be modified when doing the 2nd+ round + * iteration to have real data pushed to the stream. + */ + args->live = true; + + args->start.caps[MIGRATION_CAPABILITY_XBZRLE] = true; + + test_precopy_common(args); } static void * @@ -XXX,XX +XXX,XX @@ migrate_hook_start_precopy_tcp_multifd_zlib(QTestState *from, return migrate_hook_start_precopy_tcp_multifd_common(from, to, "zlib"); } -static void test_multifd_tcp_zlib(void) +static void test_multifd_tcp_zlib(char *name, MigrateCommon *args) { - MigrateCommon args = { - .listen_uri = "defer", - .start = { - .caps[MIGRATION_CAPABILITY_MULTIFD] = true, - }, - .start_hook = migrate_hook_start_precopy_tcp_multifd_zlib, - }; - test_precopy_common(&args); + args->listen_uri = "defer"; + args->start_hook = migrate_hook_start_precopy_tcp_multifd_zlib; + + args->start.caps[MIGRATION_CAPABILITY_MULTIFD] = true; + + test_precopy_common(args); } static void migration_test_add_compression_smoke(MigrationTestEnv *env) diff --git a/tests/qtest/migration/cpr-tests.c b/tests/qtest/migration/cpr-tests.c index XXXXXXX..XXXXXXX 100644 --- a/tests/qtest/migration/cpr-tests.c +++ b/tests/qtest/migration/cpr-tests.c @@ -XXX,XX +XXX,XX @@ static void *migrate_hook_start_mode_reboot(QTestState *from, QTestState *to) return NULL; } -static void test_mode_reboot(void) +static void test_mode_reboot(char *name, MigrateCommon *args) { g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs, FILE_TEST_FILENAME); - MigrateCommon args = { - .start.mem_type = MEM_TYPE_SHMEM, - .connect_uri = uri, - .listen_uri = "defer", - .start_hook = migrate_hook_start_mode_reboot, - .start = { - .caps[MIGRATION_CAPABILITY_X_IGNORE_SHARED] = true, - }, - }; - - test_file_common(&args, true); + + args->connect_uri = uri; + args->listen_uri = "defer"; + args->start_hook = migrate_hook_start_mode_reboot; + + args->start.mem_type = MEM_TYPE_SHMEM; + args->start.caps[MIGRATION_CAPABILITY_X_IGNORE_SHARED] = true; + + test_file_common(args, true); } static void *test_mode_transfer_start(QTestState *from, QTestState *to) @@ -XXX,XX +XXX,XX @@ static void *test_mode_transfer_start(QTestState *from, QTestState *to) * migration, and cannot connect synchronously to the monitor, so defer * the target connection. */ -static void test_mode_transfer_common(bool incoming_defer) +static void test_mode_transfer_common(MigrateCommon *args, bool incoming_defer) { g_autofree char *cpr_path = g_strdup_printf("%s/cpr.sock", tmpfs); g_autofree char *mig_path = g_strdup_printf("%s/migsocket", tmpfs); @@ -XXX,XX +XXX,XX @@ static void test_mode_transfer_common(bool incoming_defer) opts_target = g_strdup_printf("-incoming cpr,addr.transport=socket," "addr.type=fd,addr.str=%d %s", cpr_sockfd, opts); - MigrateCommon args = { - .start.opts_source = opts, - .start.opts_target = opts_target, - .start.defer_target_connect = true, - .start.mem_type = MEM_TYPE_MEMFD, - .listen_uri = incoming_defer ? "defer" : uri, - .connect_channels = connect_channels, - .cpr_channel = cpr_channel, - .start_hook = test_mode_transfer_start, - }; - - if (test_precopy_common(&args) < 0) { + + args->listen_uri = incoming_defer ? "defer" : uri; + args->connect_channels = connect_channels; + args->cpr_channel = cpr_channel; + args->start_hook = test_mode_transfer_start; + + args->start.opts_source = opts; + args->start.opts_target = opts_target; + args->start.defer_target_connect = true; + args->start.mem_type = MEM_TYPE_MEMFD; + + if (test_precopy_common(args) < 0) { close(cpr_sockfd); unlink(cpr_path); } } -static void test_mode_transfer(void) +static void test_mode_transfer(char *name, MigrateCommon *args) { - test_mode_transfer_common(NULL); + test_mode_transfer_common(args, false); } -static void test_mode_transfer_defer(void) +static void test_mode_transfer_defer(char *name, MigrateCommon *args) { - test_mode_transfer_common(true); + test_mode_transfer_common(args, true); } static void set_cpr_exec_args(QTestState *who, MigrateCommon *args) @@ -XXX,XX +XXX,XX @@ static void *test_mode_exec_start(QTestState *from, QTestState *to) return NULL; } -static void test_mode_exec(void) +static void test_mode_exec(char *name, MigrateCommon *args) { g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs, FILE_TEST_FILENAME); g_autofree char *listen_uri = g_strdup_printf("defer"); - MigrateCommon args = { - .start.only_source = true, - .start.opts_source = "-machine aux-ram-share=on -nodefaults", - .start.mem_type = MEM_TYPE_MEMFD, - .connect_uri = uri, - .listen_uri = listen_uri, - .start_hook = test_mode_exec_start, - }; + args->connect_uri = uri; + args->listen_uri = listen_uri; + args->start_hook = test_mode_exec_start; + + args->start.only_source = true; + args->start.opts_source = "-machine aux-ram-share=on -nodefaults"; + args->start.mem_type = MEM_TYPE_MEMFD; - test_cpr_exec(&args); + test_cpr_exec(args); } void migration_test_add_cpr(MigrationTestEnv *env) diff --git a/tests/qtest/migration/file-tests.c b/tests/qtest/migration/file-tests.c index XXXXXXX..XXXXXXX 100644 --- a/tests/qtest/migration/file-tests.c +++ b/tests/qtest/migration/file-tests.c @@ -XXX,XX +XXX,XX @@ static char *tmpfs; -static void test_precopy_file(void) +static void test_precopy_file(char *name, MigrateCommon *args) { g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs, FILE_TEST_FILENAME); - MigrateCommon args = { - .connect_uri = uri, - .listen_uri = "defer", - }; + args->connect_uri = uri; + args->listen_uri = "defer"; - test_file_common(&args, true); + test_file_common(args, true); } #ifndef _WIN32 @@ -XXX,XX +XXX,XX @@ static void *migrate_hook_start_file_offset_fdset(QTestState *from, return NULL; } -static void test_precopy_file_offset_fdset(void) +static void test_precopy_file_offset_fdset(char *name, MigrateCommon *args) { g_autofree char *uri = g_strdup_printf("file:/dev/fdset/1,offset=%d", FILE_TEST_OFFSET); - MigrateCommon args = { - .connect_uri = uri, - .listen_uri = "defer", - .start_hook = migrate_hook_start_file_offset_fdset, - }; + args->connect_uri = uri; + args->listen_uri = "defer"; + args->start_hook = migrate_hook_start_file_offset_fdset; - test_file_common(&args, false); + test_file_common(args, false); } #endif -static void test_precopy_file_offset(void) +static void test_precopy_file_offset(char *name, MigrateCommon *args) { g_autofree char *uri = g_strdup_printf("file:%s/%s,offset=%d", tmpfs, FILE_TEST_FILENAME, FILE_TEST_OFFSET); - MigrateCommon args = { - .connect_uri = uri, - .listen_uri = "defer", - }; - test_file_common(&args, false); + args->connect_uri = uri; + args->listen_uri = "defer"; + + test_file_common(args, false); } -static void test_precopy_file_offset_bad(void) +static void test_precopy_file_offset_bad(char *name, MigrateCommon *args) { /* using a value not supported by qemu_strtosz() */ g_autofree char *uri = g_strdup_printf("file:%s/%s,offset=0x20M", tmpfs, FILE_TEST_FILENAME); - MigrateCommon args = { - .connect_uri = uri, - .listen_uri = "defer", - .result = MIG_TEST_QMP_ERROR, - }; - test_file_common(&args, false); + args->connect_uri = uri; + args->listen_uri = "defer"; + args->result = MIG_TEST_QMP_ERROR; + + test_file_common(args, false); } -static void test_precopy_file_mapped_ram_live(void) +static void test_precopy_file_mapped_ram_live(char *name, MigrateCommon *args) { g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs, FILE_TEST_FILENAME); - MigrateCommon args = { - .connect_uri = uri, - .listen_uri = "defer", - .start = { - .caps[MIGRATION_CAPABILITY_MAPPED_RAM] = true, - }, - }; - - test_file_common(&args, false); + + args->connect_uri = uri; + args->listen_uri = "defer"; + + args->start.caps[MIGRATION_CAPABILITY_MAPPED_RAM] = true; + + test_file_common(args, false); } -static void test_precopy_file_mapped_ram(void) +static void test_precopy_file_mapped_ram(char *name, MigrateCommon *args) { g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs, FILE_TEST_FILENAME); - MigrateCommon args = { - .connect_uri = uri, - .listen_uri = "defer", - .start = { - .caps[MIGRATION_CAPABILITY_MAPPED_RAM] = true, - }, - }; - - test_file_common(&args, true); + + args->connect_uri = uri; + args->listen_uri = "defer"; + + args->start.caps[MIGRATION_CAPABILITY_MAPPED_RAM] = true; + + test_file_common(args, true); } -static void test_multifd_file_mapped_ram_live(void) +static void test_multifd_file_mapped_ram_live(char *name, MigrateCommon *args) { g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs, FILE_TEST_FILENAME); - MigrateCommon args = { - .connect_uri = uri, - .listen_uri = "defer", - .start = { - .caps[MIGRATION_CAPABILITY_MULTIFD] = true, - .caps[MIGRATION_CAPABILITY_MAPPED_RAM] = true, - }, - }; - - test_file_common(&args, false); + args->connect_uri = uri; + args->listen_uri = "defer"; + + args->start.caps[MIGRATION_CAPABILITY_MULTIFD] = true; + args->start.caps[MIGRATION_CAPABILITY_MAPPED_RAM] = true; + + test_file_common(args, false); } -static void test_multifd_file_mapped_ram(void) +static void test_multifd_file_mapped_ram(char *name, MigrateCommon *args) { g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs, FILE_TEST_FILENAME); - MigrateCommon args = { - .connect_uri = uri, - .listen_uri = "defer", - .start = { - .caps[MIGRATION_CAPABILITY_MULTIFD] = true, - .caps[MIGRATION_CAPABILITY_MAPPED_RAM] = true, - }, - }; - - test_file_common(&args, true); + + args->connect_uri = uri; + args->listen_uri = "defer"; + + args->start.caps[MIGRATION_CAPABILITY_MULTIFD] = true; + args->start.caps[MIGRATION_CAPABILITY_MAPPED_RAM] = true; + + test_file_common(args, true); } static void *migrate_hook_start_multifd_mapped_ram_dio(QTestState *from, @@ -XXX,XX +XXX,XX @@ static void *migrate_hook_start_multifd_mapped_ram_dio(QTestState *from, return NULL; } -static void test_multifd_file_mapped_ram_dio(void) +static void test_multifd_file_mapped_ram_dio(char *name, MigrateCommon *args) { g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs, FILE_TEST_FILENAME); - MigrateCommon args = { - .connect_uri = uri, - .listen_uri = "defer", - .start_hook = migrate_hook_start_multifd_mapped_ram_dio, - .start = { - .caps[MIGRATION_CAPABILITY_MAPPED_RAM] = true, - .caps[MIGRATION_CAPABILITY_MULTIFD] = true, - }, - }; + args->connect_uri = uri; + args->listen_uri = "defer"; + args->start_hook = migrate_hook_start_multifd_mapped_ram_dio; + + args->start.caps[MIGRATION_CAPABILITY_MAPPED_RAM] = true; + args->start.caps[MIGRATION_CAPABILITY_MULTIFD] = true; if (!probe_o_direct_support(tmpfs)) { g_test_skip("Filesystem does not support O_DIRECT"); return; } - test_file_common(&args, true); + test_file_common(args, true); } #ifndef _WIN32 @@ -XXX,XX +XXX,XX @@ static void *migrate_hook_start_multifd_mapped_ram_fdset(QTestState *from, return NULL; } -static void test_multifd_file_mapped_ram_fdset(void) +static void test_multifd_file_mapped_ram_fdset(char *name, MigrateCommon *args) { g_autofree char *uri = g_strdup_printf("file:/dev/fdset/1,offset=%d", FILE_TEST_OFFSET); - MigrateCommon args = { - .connect_uri = uri, - .listen_uri = "defer", - .start_hook = migrate_hook_start_multifd_mapped_ram_fdset, - .end_hook = migrate_hook_end_multifd_mapped_ram_fdset, - .start = { - .caps[MIGRATION_CAPABILITY_MAPPED_RAM] = true, - .caps[MIGRATION_CAPABILITY_MULTIFD] = true, - }, - }; - - test_file_common(&args, true); + + args->connect_uri = uri; + args->listen_uri = "defer"; + args->start_hook = migrate_hook_start_multifd_mapped_ram_fdset; + args->end_hook = migrate_hook_end_multifd_mapped_ram_fdset; + + args->start.caps[MIGRATION_CAPABILITY_MAPPED_RAM] = true; + args->start.caps[MIGRATION_CAPABILITY_MULTIFD] = true; + + test_file_common(args, true); } -static void test_multifd_file_mapped_ram_fdset_dio(void) +static void test_multifd_file_mapped_ram_fdset_dio(char *name, + MigrateCommon *args) { g_autofree char *uri = g_strdup_printf("file:/dev/fdset/1,offset=%d", FILE_TEST_OFFSET); - MigrateCommon args = { - .connect_uri = uri, - .listen_uri = "defer", - .start_hook = migrate_hook_start_multifd_mapped_ram_fdset_dio, - .end_hook = migrate_hook_end_multifd_mapped_ram_fdset, - .start = { - .caps[MIGRATION_CAPABILITY_MAPPED_RAM] = true, - .caps[MIGRATION_CAPABILITY_MULTIFD] = true, - }, - }; + args->connect_uri = uri; + args->listen_uri = "defer"; + args->start_hook = migrate_hook_start_multifd_mapped_ram_fdset_dio; + args->end_hook = migrate_hook_end_multifd_mapped_ram_fdset; + + args->start.caps[MIGRATION_CAPABILITY_MAPPED_RAM] = true; + args->start.caps[MIGRATION_CAPABILITY_MULTIFD] = true; if (!probe_o_direct_support(tmpfs)) { g_test_skip("Filesystem does not support O_DIRECT"); return; } - test_file_common(&args, true); + test_file_common(args, true); } #endif /* !_WIN32 */ @@ -XXX,XX +XXX,XX @@ static void migration_test_add_file_smoke(MigrationTestEnv *env) test_multifd_file_mapped_ram_dio); } -static void test_precopy_file_mapped_ram_ignore_shared(void) +static void +test_precopy_file_mapped_ram_ignore_shared(char *name, MigrateCommon *args) { g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs, FILE_TEST_FILENAME); - MigrateCommon args = { - .connect_uri = uri, - .listen_uri = "defer", - .start = { - .caps[MIGRATION_CAPABILITY_MAPPED_RAM] = true, - .caps[MIGRATION_CAPABILITY_X_IGNORE_SHARED] = true, - }, - }; - - test_file_common(&args, true); + args->connect_uri = uri; + args->listen_uri = "defer"; + + args->start.caps[MIGRATION_CAPABILITY_MAPPED_RAM] = true; + args->start.caps[MIGRATION_CAPABILITY_X_IGNORE_SHARED] = true; + + test_file_common(args, true); } void migration_test_add_file(MigrationTestEnv *env) diff --git a/tests/qtest/migration/migration-util.c b/tests/qtest/migration/migration-util.c index XXXXXXX..XXXXXXX 100644 --- a/tests/qtest/migration/migration-util.c +++ b/tests/qtest/migration/migration-util.c @@ -XXX,XX +XXX,XX @@ char *resolve_machine_version(const char *alias, const char *var1, typedef struct { char *name; - void (*func)(void); - void (*func_full)(void *); + MigrateCommon *data; + void (*func)(char *name, MigrateCommon *args); } MigrationTest; static void migration_test_destroy(gpointer data) { MigrationTest *test = (MigrationTest *)data; + g_free(test->data); g_free(test->name); g_free(test); } @@ -XXX,XX +XXX,XX @@ static void migration_test_wrapper(const void *data) { MigrationTest *test = (MigrationTest *)data; + test->data = g_new0(MigrateCommon, 1); + g_test_message("Running /%s%s", qtest_get_arch(), test->name); - test->func(); + test->func(test->name, test->data); } -void migration_test_add(const char *path, void (*fn)(void)) +void migration_test_add(const char *path, + void (*fn)(char *name, MigrateCommon *args)) { MigrationTest *test = g_new0(MigrationTest, 1); @@ -XXX,XX +XXX,XX @@ void migration_test_add(const char *path, void (*fn)(void)) migration_test_destroy); } -static void migration_test_wrapper_full(const void *data) -{ - MigrationTest *test = (MigrationTest *)data; - - g_test_message("Running /%s%s", qtest_get_arch(), test->name); - test->func_full(test->name); -} - void migration_test_add_suffix(const char *path, const char *suffix, - void (*fn)(void *)) + void (*fn)(char *name, MigrateCommon *args)) { MigrationTest *test = g_new0(MigrationTest, 1); g_assert(g_str_has_suffix(path, "/")); g_assert(!g_str_has_prefix(suffix, "/")); - test->func_full = fn; + test->func = fn; test->name = g_strconcat(path, suffix, NULL); - qtest_add_data_func_full(test->name, test, migration_test_wrapper_full, + qtest_add_data_func_full(test->name, test, migration_test_wrapper, migration_test_destroy); } diff --git a/tests/qtest/migration/misc-tests.c b/tests/qtest/migration/misc-tests.c index XXXXXXX..XXXXXXX 100644 --- a/tests/qtest/migration/misc-tests.c +++ b/tests/qtest/migration/misc-tests.c @@ -XXX,XX +XXX,XX @@ static char *tmpfs; -static void test_baddest(void) +static void test_baddest(char *name, MigrateCommon *args) { - MigrateStart args = { - .hide_stderr = true - }; QTestState *from, *to; - if (migrate_start(&from, &to, "tcp:127.0.0.1:0", &args)) { + args->start.hide_stderr = true; + + if (migrate_start(&from, &to, "tcp:127.0.0.1:0", &args->start)) { return; } migrate_qmp(from, to, "tcp:127.0.0.1:0", NULL, "{}"); @@ -XXX,XX +XXX,XX @@ static void test_baddest(void) } #ifndef _WIN32 -static void test_analyze_script(void) +static void test_analyze_script(char *name, MigrateCommon *args) { - MigrateStart args = { - .opts_source = "-uuid 11111111-1111-1111-1111-111111111111", - }; QTestState *from, *to; g_autofree char *uri = NULL; g_autofree char *file = NULL; int pid, wstatus; const char *python = g_getenv("PYTHON"); + args->start.opts_source = "-uuid 11111111-1111-1111-1111-111111111111"; + if (!python) { g_test_skip("PYTHON variable not set"); return; } /* dummy url */ - if (migrate_start(&from, &to, "tcp:127.0.0.1:0", &args)) { + if (migrate_start(&from, &to, "tcp:127.0.0.1:0", &args->start)) { return; } @@ -XXX,XX +XXX,XX @@ static void test_analyze_script(void) } #endif -static void test_ignore_shared(void) +static void test_ignore_shared(char *name, MigrateCommon *args) { g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); QTestState *from, *to; - MigrateStart args = { - .mem_type = MEM_TYPE_SHMEM, - .caps[MIGRATION_CAPABILITY_X_IGNORE_SHARED] = true, - }; - if (migrate_start(&from, &to, uri, &args)) { + args->start.mem_type = MEM_TYPE_SHMEM; + args->start.caps[MIGRATION_CAPABILITY_X_IGNORE_SHARED] = true; + + if (migrate_start(&from, &to, uri, &args->start)) { return; } @@ -XXX,XX +XXX,XX @@ static void do_test_validate_uuid(MigrateStart *args, bool should_fail) migrate_end(from, to, false); } -static void test_validate_uuid(void) +static void test_validate_uuid(char *name, MigrateCommon *args) { - MigrateStart args = { - .opts_source = "-uuid 11111111-1111-1111-1111-111111111111", - .opts_target = "-uuid 11111111-1111-1111-1111-111111111111", - }; + args->start.opts_source = "-uuid 11111111-1111-1111-1111-111111111111"; + args->start.opts_target = "-uuid 11111111-1111-1111-1111-111111111111"; - do_test_validate_uuid(&args, false); + do_test_validate_uuid(&args->start, false); } -static void test_validate_uuid_error(void) +static void test_validate_uuid_error(char *name, MigrateCommon *args) { - MigrateStart args = { - .opts_source = "-uuid 11111111-1111-1111-1111-111111111111", - .opts_target = "-uuid 22222222-2222-2222-2222-222222222222", - .hide_stderr = true, - }; + args->start.opts_source = "-uuid 11111111-1111-1111-1111-111111111111"; + args->start.opts_target = "-uuid 22222222-2222-2222-2222-222222222222"; + args->start.hide_stderr = true; - do_test_validate_uuid(&args, true); + do_test_validate_uuid(&args->start, true); } -static void test_validate_uuid_src_not_set(void) +static void test_validate_uuid_src_not_set(char *name, MigrateCommon *args) { - MigrateStart args = { - .opts_target = "-uuid 22222222-2222-2222-2222-222222222222", - .hide_stderr = true, - }; + args->start.opts_target = "-uuid 22222222-2222-2222-2222-222222222222"; + args->start.hide_stderr = true; - do_test_validate_uuid(&args, false); + do_test_validate_uuid(&args->start, false); } -static void test_validate_uuid_dst_not_set(void) +static void test_validate_uuid_dst_not_set(char *name, MigrateCommon *args) { - MigrateStart args = { - .opts_source = "-uuid 11111111-1111-1111-1111-111111111111", - .hide_stderr = true, - }; + args->start.opts_source = "-uuid 11111111-1111-1111-1111-111111111111"; + args->start.hide_stderr = true; - do_test_validate_uuid(&args, false); + do_test_validate_uuid(&args->start, false); } static void do_test_validate_uri_channel(MigrateCommon *args) @@ -XXX,XX +XXX,XX @@ static void do_test_validate_uri_channel(MigrateCommon *args) migrate_end(from, to, false); } -static void test_validate_uri_channels_both_set(void) +static void test_validate_uri_channels_both_set(char *name, MigrateCommon *args) { - MigrateCommon args = { - .start = { - .hide_stderr = true, - }, - .listen_uri = "defer", - .connect_uri = "tcp:127.0.0.1:0", - .connect_channels = ("[ { ""'channel-type': 'main'," - " 'addr': { 'transport': 'socket'," - " 'type': 'inet'," - " 'host': '127.0.0.1'," - " 'port': '0' } } ]"), - }; - - do_test_validate_uri_channel(&args); + args->listen_uri = "defer", + args->connect_uri = "tcp:127.0.0.1:0", + args->connect_channels = ("[ { ""'channel-type': 'main'," + " 'addr': { 'transport': 'socket'," + " 'type': 'inet'," + " 'host': '127.0.0.1'," + " 'port': '0' } } ]"), + + args->start.hide_stderr = true; + + do_test_validate_uri_channel(args); } -static void test_validate_uri_channels_none_set(void) +static void test_validate_uri_channels_none_set(char *name, MigrateCommon *args) { - MigrateCommon args = { - .start = { - .hide_stderr = true, - }, - .listen_uri = "defer", - }; - - do_test_validate_uri_channel(&args); + args->listen_uri = "defer"; + args->start.hide_stderr = true; + + do_test_validate_uri_channel(args); } static void migration_test_add_misc_smoke(MigrationTestEnv *env) diff --git a/tests/qtest/migration/postcopy-tests.c b/tests/qtest/migration/postcopy-tests.c index XXXXXXX..XXXXXXX 100644 --- a/tests/qtest/migration/postcopy-tests.c +++ b/tests/qtest/migration/postcopy-tests.c @@ -XXX,XX +XXX,XX @@ #include "qemu/range.h" #include "qemu/sockets.h" -static void test_postcopy(void) +static void test_postcopy(char *name, MigrateCommon *args) { - MigrateCommon args = { }; - - test_postcopy_common(&args); + test_postcopy_common(args); } -static void test_postcopy_suspend(void) +static void test_postcopy_suspend(char *name, MigrateCommon *args) { - MigrateCommon args = { - .start.suspend_me = true, - }; + args->start.suspend_me = true; - test_postcopy_common(&args); + test_postcopy_common(args); } -static void test_postcopy_preempt(void) +static void test_postcopy_preempt(char *name, MigrateCommon *args) { - MigrateCommon args = { - .start = { - .caps[MIGRATION_CAPABILITY_POSTCOPY_PREEMPT] = true, - }, - }; + args->start.caps[MIGRATION_CAPABILITY_POSTCOPY_PREEMPT] = true; - test_postcopy_common(&args); + test_postcopy_common(args); } -static void test_postcopy_recovery(void) +static void test_postcopy_recovery(char *name, MigrateCommon *args) { - MigrateCommon args = { }; - - test_postcopy_recovery_common(&args); + test_postcopy_recovery_common(args); } -static void test_postcopy_recovery_fail_handshake(void) +static void test_postcopy_recovery_fail_handshake(char *name, + MigrateCommon *args) { - MigrateCommon args = { - .postcopy_recovery_fail_stage = POSTCOPY_FAIL_RECOVERY, - }; + args->postcopy_recovery_fail_stage = POSTCOPY_FAIL_RECOVERY; - test_postcopy_recovery_common(&args); + test_postcopy_recovery_common(args); } -static void test_postcopy_recovery_fail_reconnect(void) +static void test_postcopy_recovery_fail_reconnect(char *name, + MigrateCommon *args) { - MigrateCommon args = { - .postcopy_recovery_fail_stage = POSTCOPY_FAIL_CHANNEL_ESTABLISH, - }; + args->postcopy_recovery_fail_stage = POSTCOPY_FAIL_CHANNEL_ESTABLISH; - test_postcopy_recovery_common(&args); + test_postcopy_recovery_common(args); } -static void test_postcopy_preempt_recovery(void) +static void test_postcopy_preempt_recovery(char *name, MigrateCommon *args) { - MigrateCommon args = { - .start = { - .caps[MIGRATION_CAPABILITY_POSTCOPY_PREEMPT] = true, - }, - }; + args->start.caps[MIGRATION_CAPABILITY_POSTCOPY_PREEMPT] = true; - test_postcopy_recovery_common(&args); + test_postcopy_recovery_common(args); } static void migration_test_add_postcopy_smoke(MigrationTestEnv *env) @@ -XXX,XX +XXX,XX @@ static void migration_test_add_postcopy_smoke(MigrationTestEnv *env) } } -static void test_multifd_postcopy(void) +static void test_multifd_postcopy(char *name, MigrateCommon *args) { - MigrateCommon args = { - .start = { - .caps[MIGRATION_CAPABILITY_MULTIFD] = true, - }, - }; + args->start.caps[MIGRATION_CAPABILITY_MULTIFD] = true; - test_postcopy_common(&args); + test_postcopy_common(args); } -static void test_multifd_postcopy_preempt(void) +static void test_multifd_postcopy_preempt(char *name, MigrateCommon *args) { - MigrateCommon args = { - .start = { - .caps[MIGRATION_CAPABILITY_MULTIFD] = true, - .caps[MIGRATION_CAPABILITY_POSTCOPY_PREEMPT] = true, - }, - }; - - test_postcopy_common(&args); + args->start.caps[MIGRATION_CAPABILITY_MULTIFD] = true; + args->start.caps[MIGRATION_CAPABILITY_POSTCOPY_PREEMPT] = true; + + test_postcopy_common(args); } void migration_test_add_postcopy(MigrationTestEnv *env) diff --git a/tests/qtest/migration/precopy-tests.c b/tests/qtest/migration/precopy-tests.c index XXXXXXX..XXXXXXX 100644 --- a/tests/qtest/migration/precopy-tests.c +++ b/tests/qtest/migration/precopy-tests.c @@ -XXX,XX +XXX,XX @@ static char *tmpfs; -static void test_precopy_unix_plain(void) +static void test_precopy_unix_plain(char *name, MigrateCommon *args) { g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); - MigrateCommon args = { - .listen_uri = uri, - .connect_uri = uri, - /* - * The simplest use case of precopy, covering smoke tests of - * get-dirty-log dirty tracking. - */ - .live = true, - }; - test_precopy_common(&args); + args->listen_uri = uri; + args->connect_uri = uri; + /* + * The simplest use case of precopy, covering smoke tests of + * get-dirty-log dirty tracking. + */ + args->live = true; + + test_precopy_common(args); } -static void test_precopy_unix_suspend_live(void) +static void test_precopy_unix_suspend_live(char *name, MigrateCommon *args) { g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); - MigrateCommon args = { - .listen_uri = uri, - .connect_uri = uri, - /* - * despite being live, the test is fast because the src - * suspends immediately. - */ - .live = true, - .start.suspend_me = true, - }; - test_precopy_common(&args); + args->listen_uri = uri; + args->connect_uri = uri; + /* + * despite being live, the test is fast because the src + * suspends immediately. + */ + args->live = true; + + args->start.suspend_me = true; + + test_precopy_common(args); } -static void test_precopy_unix_suspend_notlive(void) +static void test_precopy_unix_suspend_notlive(char *name, MigrateCommon *args) { g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); - MigrateCommon args = { - .listen_uri = uri, - .connect_uri = uri, - .start.suspend_me = true, - }; - test_precopy_common(&args); + args->listen_uri = uri; + args->connect_uri = uri; + args->start.suspend_me = true; + + test_precopy_common(args); } -static void test_precopy_unix_dirty_ring(void) +static void test_precopy_unix_dirty_ring(char *name, MigrateCommon *args) { g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); - MigrateCommon args = { - .start = { - .use_dirty_ring = true, - }, - .listen_uri = uri, - .connect_uri = uri, - /* - * Besides the precopy/unix basic test, cover dirty ring interface - * rather than get-dirty-log. - */ - .live = true, - }; - test_precopy_common(&args); + args->listen_uri = uri; + args->connect_uri = uri; + /* + * Besides the precopy/unix basic test, cover dirty ring interface + * rather than get-dirty-log. + */ + args->live = true; + + args->start.use_dirty_ring = true; + + test_precopy_common(args); } #ifdef CONFIG_RDMA @@ -XXX,XX +XXX,XX @@ static int new_rdma_link(char *buffer, bool ipv6) return -1; } -static void __test_precopy_rdma_plain(bool ipv6) +static void __test_precopy_rdma_plain(MigrateCommon *args, bool ipv6) { char buffer[128] = {}; @@ -XXX,XX +XXX,XX @@ static void __test_precopy_rdma_plain(bool ipv6) **/ g_autofree char *uri = g_strdup_printf("rdma:%s:29200", buffer); - MigrateCommon args = { - .listen_uri = uri, - .connect_uri = uri, - }; + args->listen_uri = uri; + args->connect_uri = uri; - test_precopy_common(&args); + test_precopy_common(args); } -static void test_precopy_rdma_plain(void) +static void test_precopy_rdma_plain(char *name, MigrateCommon *args) { - __test_precopy_rdma_plain(false); + __test_precopy_rdma_plain(args, false); } -static void test_precopy_rdma_plain_ipv6(void) +static void test_precopy_rdma_plain_ipv6(char *name, MigrateCommon *args) { - __test_precopy_rdma_plain(true); + __test_precopy_rdma_plain(args, true); } #endif -static void test_precopy_tcp_plain(void) +static void test_precopy_tcp_plain(char *name, MigrateCommon *args) { - MigrateCommon args = { - .listen_uri = "tcp:127.0.0.1:0", - }; + args->listen_uri = "tcp:127.0.0.1:0"; - test_precopy_common(&args); + test_precopy_common(args); } -static void test_precopy_tcp_switchover_ack(void) +static void test_precopy_tcp_switchover_ack(char *name, MigrateCommon *args) { - MigrateCommon args = { - .listen_uri = "tcp:127.0.0.1:0", - .start = { - .caps[MIGRATION_CAPABILITY_RETURN_PATH] = true, - .caps[MIGRATION_CAPABILITY_SWITCHOVER_ACK] = true, - }, - /* - * Source VM must be running in order to consider the switchover ACK - * when deciding to do switchover or not. - */ - .live = true, - }; + args->listen_uri = "tcp:127.0.0.1:0"; + /* + * Source VM must be running in order to consider the switchover ACK + * when deciding to do switchover or not. + */ + args->live = true; - test_precopy_common(&args); + args->start.caps[MIGRATION_CAPABILITY_RETURN_PATH] = true; + args->start.caps[MIGRATION_CAPABILITY_SWITCHOVER_ACK] = true; + + test_precopy_common(args); } #ifndef _WIN32 @@ -XXX,XX +XXX,XX @@ static void migrate_hook_end_fd(QTestState *from, qobject_unref(rsp); } -static void test_precopy_fd_socket(void) +static void test_precopy_fd_socket(char *name, MigrateCommon *args) { - MigrateCommon args = { - .listen_uri = "defer", - .connect_uri = "fd:fd-mig", - .start_hook = migrate_hook_start_fd, - .end_hook = migrate_hook_end_fd, - }; - test_precopy_common(&args); + args->listen_uri = "defer"; + args->connect_uri = "fd:fd-mig"; + args->start_hook = migrate_hook_start_fd; + args->end_hook = migrate_hook_end_fd; + + test_precopy_common(args); } static void *migrate_hook_start_precopy_fd_file(QTestState *from, @@ -XXX,XX +XXX,XX @@ static void *migrate_hook_start_precopy_fd_file(QTestState *from, return NULL; } -static void test_precopy_fd_file(void) +static void test_precopy_fd_file(char *name, MigrateCommon *args) { - MigrateCommon args = { - .listen_uri = "defer", - .connect_uri = "fd:fd-mig", - .start_hook = migrate_hook_start_precopy_fd_file, - .end_hook = migrate_hook_end_fd, - }; - test_file_common(&args, true); + args->listen_uri = "defer"; + args->connect_uri = "fd:fd-mig"; + args->start_hook = migrate_hook_start_precopy_fd_file; + args->end_hook = migrate_hook_end_fd; + + test_file_common(args, true); } #endif /* _WIN32 */ @@ -XXX,XX +XXX,XX @@ static void test_precopy_fd_file(void) * To make things even worse, we need to run the initial stage at * 3MB/s so we enter autoconverge even when host is (over)loaded. */ -static void test_auto_converge(void) +static void test_auto_converge(char *name, MigrateCommon *args) { g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); - MigrateStart args = {}; QTestState *from, *to; int64_t percentage; @@ -XXX,XX +XXX,XX @@ static void test_auto_converge(void) uint64_t prev_dirty_sync_cnt, dirty_sync_cnt; int max_try_count, hit = 0; - if (migrate_start(&from, &to, uri, &args)) { + if (migrate_start(&from, &to, uri, &args->start)) { return; } @@ -XXX,XX +XXX,XX @@ migrate_hook_start_precopy_tcp_multifd_no_zero_page(QTestState *from, return NULL; } -static void test_multifd_tcp_uri_none(void) +static void test_multifd_tcp_uri_none(char *name, MigrateCommon *args) { - MigrateCommon args = { - .listen_uri = "defer", - .start_hook = migrate_hook_start_precopy_tcp_multifd, - .start = { - .caps[MIGRATION_CAPABILITY_MULTIFD] = true, - }, - /* - * Multifd is more complicated than most of the features, it - * directly takes guest page buffers when sending, make sure - * everything will work alright even if guest page is changing. - */ - .live = true, - }; - test_precopy_common(&args); + args->listen_uri = "defer"; + args->start_hook = migrate_hook_start_precopy_tcp_multifd; + /* + * Multifd is more complicated than most of the features, it + * directly takes guest page buffers when sending, make sure + * everything will work alright even if guest page is changing. + */ + args->live = true; + + args->start.caps[MIGRATION_CAPABILITY_MULTIFD] = true; + + test_precopy_common(args); } -static void test_multifd_tcp_zero_page_legacy(void) +static void test_multifd_tcp_zero_page_legacy(char *name, MigrateCommon *args) { - MigrateCommon args = { - .listen_uri = "defer", - .start_hook = migrate_hook_start_precopy_tcp_multifd_zero_page_legacy, - .start = { - .caps[MIGRATION_CAPABILITY_MULTIFD] = true, - }, - /* - * Multifd is more complicated than most of the features, it - * directly takes guest page buffers when sending, make sure - * everything will work alright even if guest page is changing. - */ - .live = true, - }; - test_precopy_common(&args); + args->listen_uri = "defer"; + args->start_hook = migrate_hook_start_precopy_tcp_multifd_zero_page_legacy; + /* + * Multifd is more complicated than most of the features, it + * directly takes guest page buffers when sending, make sure + * everything will work alright even if guest page is changing. + */ + args->live = true; + + args->start.caps[MIGRATION_CAPABILITY_MULTIFD] = true; + + test_precopy_common(args); } -static void test_multifd_tcp_no_zero_page(void) +static void test_multifd_tcp_no_zero_page(char *name, MigrateCommon *args) { - MigrateCommon args = { - .listen_uri = "defer", - .start_hook = migrate_hook_start_precopy_tcp_multifd_no_zero_page, - .start = { - .caps[MIGRATION_CAPABILITY_MULTIFD] = true, - }, - /* - * Multifd is more complicated than most of the features, it - * directly takes guest page buffers when sending, make sure - * everything will work alright even if guest page is changing. - */ - .live = true, - }; - test_precopy_common(&args); + args->listen_uri = "defer"; + args->start_hook = migrate_hook_start_precopy_tcp_multifd_no_zero_page; + /* + * Multifd is more complicated than most of the features, it + * directly takes guest page buffers when sending, make sure + * everything will work alright even if guest page is changing. + */ + args->live = true; + + args->start.caps[MIGRATION_CAPABILITY_MULTIFD] = true; + + test_precopy_common(args); } -static void test_multifd_tcp_channels_none(void) +static void test_multifd_tcp_channels_none(char *name, MigrateCommon *args) { - MigrateCommon args = { - .listen_uri = "defer", - .start_hook = migrate_hook_start_precopy_tcp_multifd, - .live = true, - .start = { - .caps[MIGRATION_CAPABILITY_MULTIFD] = true, - }, - .connect_channels = ("[ { 'channel-type': 'main'," + args->listen_uri = "defer"; + args->start_hook = migrate_hook_start_precopy_tcp_multifd; + args->live = true; + args->connect_channels = ("[ { 'channel-type': 'main'," " 'addr': { 'transport': 'socket'," " 'type': 'inet'," " 'host': '127.0.0.1'," - " 'port': '0' } } ]"), - }; - test_precopy_common(&args); + " 'port': '0' } } ]"); + + args->start.caps[MIGRATION_CAPABILITY_MULTIFD] = true; + + test_precopy_common(args); } /* @@ -XXX,XX +XXX,XX @@ static void test_multifd_tcp_channels_none(void) * * And see that it works */ -static void test_multifd_tcp_cancel(bool postcopy_ram) +static void test_multifd_tcp_cancel(MigrateCommon *args, bool postcopy_ram) { - MigrateStart args = { - .hide_stderr = true, - }; QTestState *from, *to, *to2; - if (migrate_start(&from, &to, "defer", &args)) { + args->start.hide_stderr = true; + + if (migrate_start(&from, &to, "defer", &args->start)) { return; } @@ -XXX,XX +XXX,XX @@ static void test_multifd_tcp_cancel(bool postcopy_ram) */ wait_for_migration_status(from, "cancelled", NULL); - args = (MigrateStart){ - .only_target = true, - }; + args->start.only_target = true; - if (migrate_start(&from, &to2, "defer", &args)) { + if (migrate_start(&from, &to2, "defer", &args->start)) { return; } @@ -XXX,XX +XXX,XX @@ static void test_multifd_tcp_cancel(bool postcopy_ram) migrate_end(from, to2, true); } -static void test_multifd_precopy_tcp_cancel(void) +static void test_multifd_precopy_tcp_cancel(char *name, MigrateCommon *args) { - test_multifd_tcp_cancel(false); + test_multifd_tcp_cancel(args, false); } -static void test_multifd_postcopy_tcp_cancel(void) +static void test_multifd_postcopy_tcp_cancel(char *name, MigrateCommon *args) { - test_multifd_tcp_cancel(true); + test_multifd_tcp_cancel(args, true); } static void test_cancel_src_after_failed(QTestState *from, QTestState *to, @@ -XXX,XX +XXX,XX @@ static void test_cancel_src_pre_switchover(QTestState *from, QTestState *to, (const char * []) { "completed", NULL }); } -static void test_cancel_src_after_status(void *opaque) +static void test_cancel_src_after_status(char *test_path, MigrateCommon *args) { - const char *test_path = opaque; g_autofree char *phase = g_path_get_basename(test_path); g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); QTestState *from, *to; - MigrateStart args = { - .hide_stderr = true, - }; - if (migrate_start(&from, &to, "defer", &args)) { + args->start.hide_stderr = true; + + if (migrate_start(&from, &to, "defer", &args->start)) { return; } @@ -XXX,XX +XXX,XX @@ static void dirtylimit_stop_vm(QTestState *vm) unlink(path); } -static void test_vcpu_dirty_limit(void) +static void test_vcpu_dirty_limit(char *name, MigrateCommon *args) { QTestState *vm; int64_t origin_rate; @@ -XXX,XX +XXX,XX @@ static void migrate_dirty_limit_wait_showup(QTestState *from, * And see if dirty limit migration works correctly. * This test case involves many passes, so it runs in slow mode only. */ -static void test_dirty_limit(void) +static void test_dirty_limit(char *name, MigrateCommon *args) { g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); QTestState *from, *to; @@ -XXX,XX +XXX,XX @@ static void test_dirty_limit(void) */ const int64_t expected_threshold = max_bandwidth * downtime_limit / 1000; int max_try_count = 10; - MigrateCommon args = { - .start = { - .hide_stderr = true, - .use_dirty_ring = true, - }, - .listen_uri = uri, - .connect_uri = uri, - }; + + args->start.hide_stderr = true; + args->start.use_dirty_ring = true; + + args->listen_uri = uri; + args->connect_uri = uri; /* Start src, dst vm */ - if (migrate_start(&from, &to, args.listen_uri, &args.start)) { + if (migrate_start(&from, &to, args->listen_uri, &args->start)) { return; } @@ -XXX,XX +XXX,XX @@ static void test_dirty_limit(void) migrate_dirty_limit_wait_showup(from, dirtylimit_period, dirtylimit_value); /* Start migrate */ - migrate_qmp(from, to, args.connect_uri, NULL, "{}"); + migrate_qmp(from, to, args->connect_uri, NULL, "{}"); /* Wait for dirty limit throttle begin */ throttle_us_per_full = 0; @@ -XXX,XX +XXX,XX @@ static void test_dirty_limit(void) /* Assert dirty limit is not in service */ g_assert_cmpint(throttle_us_per_full, ==, 0); - args = (MigrateCommon) { - .start = { - .only_target = true, - .use_dirty_ring = true, - }, - .listen_uri = uri, - .connect_uri = uri, - }; + args->listen_uri = uri; + args->connect_uri = uri; + + args->start.only_target = true; + args->start.use_dirty_ring = true; /* Restart dst vm, src vm already show up so we needn't wait anymore */ - if (migrate_start(&from, &to, args.listen_uri, &args.start)) { + if (migrate_start(&from, &to, args->listen_uri, &args->start)) { return; } /* Start migrate */ - migrate_qmp(from, to, args.connect_uri, NULL, "{}"); + migrate_qmp(from, to, args->connect_uri, NULL, "{}"); /* Wait for dirty limit throttle begin */ throttle_us_per_full = 0; diff --git a/tests/qtest/migration/tls-tests.c b/tests/qtest/migration/tls-tests.c index XXXXXXX..XXXXXXX 100644 --- a/tests/qtest/migration/tls-tests.c +++ b/tests/qtest/migration/tls-tests.c @@ -XXX,XX +XXX,XX @@ migrate_hook_end_tls_x509(QTestState *from, } #endif /* CONFIG_TASN1 */ -static void test_postcopy_tls_psk(void) +static void test_postcopy_tls_psk(char *name, MigrateCommon *args) { - MigrateCommon args = { - .start_hook = migrate_hook_start_tls_psk_match, - .end_hook = migrate_hook_end_tls_psk, - }; + args->start_hook = migrate_hook_start_tls_psk_match; + args->end_hook = migrate_hook_end_tls_psk; - test_postcopy_common(&args); + test_postcopy_common(args); } -static void test_postcopy_preempt_tls_psk(void) +static void test_postcopy_preempt_tls_psk(char *name, MigrateCommon *args) { - MigrateCommon args = { - .start_hook = migrate_hook_start_tls_psk_match, - .end_hook = migrate_hook_end_tls_psk, - .start = { - .caps[MIGRATION_CAPABILITY_POSTCOPY_PREEMPT] = true, - }, - }; + args->start_hook = migrate_hook_start_tls_psk_match; + args->end_hook = migrate_hook_end_tls_psk; - test_postcopy_common(&args); + args->start.caps[MIGRATION_CAPABILITY_POSTCOPY_PREEMPT] = true; + + test_postcopy_common(args); } -static void test_postcopy_recovery_tls_psk(void) +static void test_postcopy_recovery_tls_psk(char *name, MigrateCommon *args) { - MigrateCommon args = { - .start_hook = migrate_hook_start_tls_psk_match, - .end_hook = migrate_hook_end_tls_psk, - }; + args->start_hook = migrate_hook_start_tls_psk_match; + args->end_hook = migrate_hook_end_tls_psk; - test_postcopy_recovery_common(&args); + test_postcopy_recovery_common(args); } -static void test_multifd_postcopy_recovery_tls_psk(void) +static void test_multifd_postcopy_recovery_tls_psk(char *name, + MigrateCommon *args) { - MigrateCommon args = { - .start_hook = migrate_hook_start_tls_psk_match, - .end_hook = migrate_hook_end_tls_psk, - .start = { - .caps[MIGRATION_CAPABILITY_MULTIFD] = true, - }, - }; + args->start_hook = migrate_hook_start_tls_psk_match; + args->end_hook = migrate_hook_end_tls_psk; + + args->start.caps[MIGRATION_CAPABILITY_MULTIFD] = true; - test_postcopy_recovery_common(&args); + test_postcopy_recovery_common(args); } /* This contains preempt+recovery+tls test altogether */ -static void test_postcopy_preempt_all(void) -{ - MigrateCommon args = { - .start_hook = migrate_hook_start_tls_psk_match, - .end_hook = migrate_hook_end_tls_psk, - .start = { - .caps[MIGRATION_CAPABILITY_POSTCOPY_PREEMPT] = true, - }, - }; +static void test_postcopy_preempt_all(char *name, MigrateCommon *args) +{ + args->start_hook = migrate_hook_start_tls_psk_match; + args->end_hook = migrate_hook_end_tls_psk; - test_postcopy_recovery_common(&args); + args->start.caps[MIGRATION_CAPABILITY_POSTCOPY_PREEMPT] = true; + + test_postcopy_recovery_common(args); } -static void test_multifd_postcopy_preempt_recovery_tls_psk(void) +static void test_multifd_postcopy_preempt_recovery_tls_psk(char *name, + MigrateCommon *args) { - 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, - }, - }; + args->start_hook = migrate_hook_start_tls_psk_match; + args->end_hook = migrate_hook_end_tls_psk; - test_postcopy_recovery_common(&args); + args->start.caps[MIGRATION_CAPABILITY_MULTIFD] = true; + args->start.caps[MIGRATION_CAPABILITY_POSTCOPY_PREEMPT] = true; + + test_postcopy_recovery_common(args); } -static void test_precopy_unix_tls_psk(void) +static void test_precopy_unix_tls_psk(char *name, MigrateCommon *args) { g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); - MigrateCommon args = { - .connect_uri = uri, - .listen_uri = uri, - .start_hook = migrate_hook_start_tls_psk_match, - .end_hook = migrate_hook_end_tls_psk, - }; - test_precopy_common(&args); + args->connect_uri = uri; + args->listen_uri = uri; + args->start_hook = migrate_hook_start_tls_psk_match; + args->end_hook = migrate_hook_end_tls_psk; + + test_precopy_common(args); } #ifdef CONFIG_TASN1 -static void test_precopy_unix_tls_x509_default_host(void) +static void test_precopy_unix_tls_x509_default_host(char *name, + MigrateCommon *args) { g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); - MigrateCommon args = { - .start = { - .hide_stderr = true, - }, - .connect_uri = uri, - .listen_uri = uri, - .start_hook = migrate_hook_start_tls_x509_default_host, - .end_hook = migrate_hook_end_tls_x509, - .result = MIG_TEST_FAIL_DEST_QUIT_ERR, - }; - test_precopy_common(&args); + args->connect_uri = uri; + args->listen_uri = uri; + args->start_hook = migrate_hook_start_tls_x509_default_host; + args->end_hook = migrate_hook_end_tls_x509; + args->result = MIG_TEST_FAIL_DEST_QUIT_ERR; + + args->start.hide_stderr = true; + + test_precopy_common(args); } -static void test_precopy_unix_tls_x509_override_host(void) +static void test_precopy_unix_tls_x509_override_host(char *name, + MigrateCommon *args) { g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); - MigrateCommon args = { - .connect_uri = uri, - .listen_uri = uri, - .start_hook = migrate_hook_start_tls_x509_override_host, - .end_hook = migrate_hook_end_tls_x509, - }; - test_precopy_common(&args); + args->connect_uri = uri; + args->listen_uri = uri; + args->start_hook = migrate_hook_start_tls_x509_override_host; + args->end_hook = migrate_hook_end_tls_x509; + + test_precopy_common(args); } #endif /* CONFIG_TASN1 */ -static void test_precopy_tcp_tls_psk_match(void) +static void test_precopy_tcp_tls_psk_match(char *name, MigrateCommon *args) { - MigrateCommon args = { - .listen_uri = "tcp:127.0.0.1:0", - .start_hook = migrate_hook_start_tls_psk_match, - .end_hook = migrate_hook_end_tls_psk, - }; + args->listen_uri = "tcp:127.0.0.1:0"; + args->start_hook = migrate_hook_start_tls_psk_match; + args->end_hook = migrate_hook_end_tls_psk; - test_precopy_common(&args); + test_precopy_common(args); } -static void test_precopy_tcp_tls_psk_mismatch(void) +static void test_precopy_tcp_tls_psk_mismatch(char *name, MigrateCommon *args) { - MigrateCommon args = { - .start = { - .hide_stderr = true, - }, - .listen_uri = "tcp:127.0.0.1:0", - .start_hook = migrate_hook_start_tls_psk_mismatch, - .end_hook = migrate_hook_end_tls_psk, - .result = MIG_TEST_FAIL, - }; + args->listen_uri = "tcp:127.0.0.1:0"; + args->start_hook = migrate_hook_start_tls_psk_mismatch; + args->end_hook = migrate_hook_end_tls_psk; + args->result = MIG_TEST_FAIL; + + args->start.hide_stderr = true; - test_precopy_common(&args); + test_precopy_common(args); } static void *migrate_hook_start_no_tls(QTestState *from, QTestState *to) @@ -XXX,XX +XXX,XX @@ static void *migrate_hook_start_no_tls(QTestState *from, QTestState *to) return data; } -static void test_precopy_tcp_no_tls(void) +static void test_precopy_tcp_no_tls(char *name, MigrateCommon *args) { - MigrateCommon args = { - .listen_uri = "tcp:127.0.0.1:0", - .start_hook = migrate_hook_start_no_tls, - .end_hook = migrate_hook_end_tls_psk, - }; + args->listen_uri = "tcp:127.0.0.1:0"; + args->start_hook = migrate_hook_start_no_tls; + args->end_hook = migrate_hook_end_tls_psk; - test_precopy_common(&args); + test_precopy_common(args); } static void * @@ -XXX,XX +XXX,XX @@ migrate_hook_start_tls_x509_no_host(QTestState *from, QTestState *to) return data; } -static void test_precopy_tcp_tls_no_hostname(void) +static void test_precopy_tcp_tls_no_hostname(char *name, MigrateCommon *args) { - MigrateCommon args = { - .listen_uri = "tcp:127.0.0.1:0", - .start_hook = migrate_hook_start_tls_x509_no_host, - .end_hook = migrate_hook_end_tls_x509, - .result = MIG_TEST_FAIL_DEST_QUIT_ERR, - .start.hide_stderr = true, - }; + args->listen_uri = "tcp:127.0.0.1:0"; + args->start_hook = migrate_hook_start_tls_x509_no_host; + args->end_hook = migrate_hook_end_tls_x509; + args->result = MIG_TEST_FAIL_DEST_QUIT_ERR; + + args->start.hide_stderr = true; - test_precopy_common(&args); + test_precopy_common(args); } #ifdef CONFIG_TASN1 -static void test_precopy_tcp_tls_x509_default_host(void) +static void test_precopy_tcp_tls_x509_default_host(char *name, + MigrateCommon *args) { - MigrateCommon args = { - .listen_uri = "tcp:127.0.0.1:0", - .start_hook = migrate_hook_start_tls_x509_default_host, - .end_hook = migrate_hook_end_tls_x509, - }; + args->listen_uri = "tcp:127.0.0.1:0"; + args->start_hook = migrate_hook_start_tls_x509_default_host; + args->end_hook = migrate_hook_end_tls_x509; - test_precopy_common(&args); + test_precopy_common(args); } -static void test_precopy_tcp_tls_x509_override_host(void) +static void test_precopy_tcp_tls_x509_override_host(char *name, + MigrateCommon *args) { - MigrateCommon args = { - .listen_uri = "tcp:127.0.0.1:0", - .start_hook = migrate_hook_start_tls_x509_override_host, - .end_hook = migrate_hook_end_tls_x509, - }; + args->listen_uri = "tcp:127.0.0.1:0"; + args->start_hook = migrate_hook_start_tls_x509_override_host; + args->end_hook = migrate_hook_end_tls_x509; - test_precopy_common(&args); + test_precopy_common(args); } -static void test_precopy_tcp_tls_x509_mismatch_host(void) +static void test_precopy_tcp_tls_x509_mismatch_host(char *name, + MigrateCommon *args) { - MigrateCommon args = { - .start = { - .hide_stderr = true, - }, - .listen_uri = "tcp:127.0.0.1:0", - .start_hook = migrate_hook_start_tls_x509_mismatch_host, - .end_hook = migrate_hook_end_tls_x509, - .result = MIG_TEST_FAIL_DEST_QUIT_ERR, - }; + args->listen_uri = "tcp:127.0.0.1:0"; + args->start_hook = migrate_hook_start_tls_x509_mismatch_host; + args->end_hook = migrate_hook_end_tls_x509; + args->result = MIG_TEST_FAIL_DEST_QUIT_ERR; + + args->start.hide_stderr = true; - test_precopy_common(&args); + test_precopy_common(args); } -static void test_precopy_tcp_tls_x509_friendly_client(void) +static void test_precopy_tcp_tls_x509_friendly_client(char *name, + MigrateCommon *args) { - MigrateCommon args = { - .listen_uri = "tcp:127.0.0.1:0", - .start_hook = migrate_hook_start_tls_x509_friendly_client, - .end_hook = migrate_hook_end_tls_x509, - }; + args->listen_uri = "tcp:127.0.0.1:0"; + args->start_hook = migrate_hook_start_tls_x509_friendly_client; + args->end_hook = migrate_hook_end_tls_x509; - test_precopy_common(&args); + test_precopy_common(args); } -static void test_precopy_tcp_tls_x509_hostile_client(void) +static void test_precopy_tcp_tls_x509_hostile_client(char *name, + MigrateCommon *args) { - MigrateCommon args = { - .start = { - .hide_stderr = true, - }, - .listen_uri = "tcp:127.0.0.1:0", - .start_hook = migrate_hook_start_tls_x509_hostile_client, - .end_hook = migrate_hook_end_tls_x509, - .result = MIG_TEST_FAIL, - }; + args->listen_uri = "tcp:127.0.0.1:0"; + args->start_hook = migrate_hook_start_tls_x509_hostile_client; + args->end_hook = migrate_hook_end_tls_x509; + args->result = MIG_TEST_FAIL; + + args->start.hide_stderr = true; - test_precopy_common(&args); + test_precopy_common(args); } -static void test_precopy_tcp_tls_x509_allow_anon_client(void) +static void test_precopy_tcp_tls_x509_allow_anon_client(char *name, + MigrateCommon *args) { - MigrateCommon args = { - .listen_uri = "tcp:127.0.0.1:0", - .start_hook = migrate_hook_start_tls_x509_allow_anon_client, - .end_hook = migrate_hook_end_tls_x509, - }; + args->listen_uri = "tcp:127.0.0.1:0"; + args->start_hook = migrate_hook_start_tls_x509_allow_anon_client; + args->end_hook = migrate_hook_end_tls_x509; - test_precopy_common(&args); + test_precopy_common(args); } -static void test_precopy_tcp_tls_x509_reject_anon_client(void) +static void test_precopy_tcp_tls_x509_reject_anon_client(char *name, + MigrateCommon *args) { - MigrateCommon args = { - .start = { - .hide_stderr = true, - }, - .listen_uri = "tcp:127.0.0.1:0", - .start_hook = migrate_hook_start_tls_x509_reject_anon_client, - .end_hook = migrate_hook_end_tls_x509, - .result = MIG_TEST_FAIL, - }; + args->listen_uri = "tcp:127.0.0.1:0"; + args->start_hook = migrate_hook_start_tls_x509_reject_anon_client; + args->end_hook = migrate_hook_end_tls_x509; + args->result = MIG_TEST_FAIL; - test_precopy_common(&args); + args->start.hide_stderr = true; + + test_precopy_common(args); } #endif /* CONFIG_TASN1 */ @@ -XXX,XX +XXX,XX @@ migrate_hook_start_multifd_tls_x509_reject_anon_client(QTestState *from, } #endif /* CONFIG_TASN1 */ -static void test_multifd_tcp_tls_psk_match(void) +static void test_multifd_tcp_tls_psk_match(char *name, MigrateCommon *args) { - MigrateCommon args = { - .listen_uri = "defer", - .start_hook = migrate_hook_start_multifd_tcp_tls_psk_match, - .end_hook = migrate_hook_end_tls_psk, - .start = { - .caps[MIGRATION_CAPABILITY_MULTIFD] = true, - }, - }; - test_precopy_common(&args); + args->listen_uri = "defer"; + args->start_hook = migrate_hook_start_multifd_tcp_tls_psk_match; + args->end_hook = migrate_hook_end_tls_psk; + + args->start.caps[MIGRATION_CAPABILITY_MULTIFD] = true; + + test_precopy_common(args); } -static void test_multifd_tcp_tls_psk_mismatch(void) +static void test_multifd_tcp_tls_psk_mismatch(char *name, MigrateCommon *args) { - MigrateCommon args = { - .start = { - .hide_stderr = true, - .caps[MIGRATION_CAPABILITY_MULTIFD] = true, - }, - .listen_uri = "defer", - .start_hook = migrate_hook_start_multifd_tcp_tls_psk_mismatch, - .end_hook = migrate_hook_end_tls_psk, - .result = MIG_TEST_FAIL, - }; - test_precopy_common(&args); + args->listen_uri = "defer"; + args->start_hook = migrate_hook_start_multifd_tcp_tls_psk_mismatch; + args->end_hook = migrate_hook_end_tls_psk; + args->result = MIG_TEST_FAIL; + + args->start.hide_stderr = true; + args->start.caps[MIGRATION_CAPABILITY_MULTIFD] = true; + + test_precopy_common(args); } -static void test_multifd_postcopy_tcp_tls_psk_match(void) +static void test_multifd_postcopy_tcp_tls_psk_match(char *name, + MigrateCommon *args) { - 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, - }; + args->listen_uri = "defer"; + args->start_hook = migrate_hook_start_multifd_tcp_tls_psk_match; + args->end_hook = migrate_hook_end_tls_psk; - test_precopy_common(&args); + args->start.caps[MIGRATION_CAPABILITY_MULTIFD] = true; + args->start.caps[MIGRATION_CAPABILITY_POSTCOPY_RAM] = true; + + test_precopy_common(args); } #ifdef CONFIG_TASN1 -static void test_multifd_tcp_tls_x509_default_host(void) -{ - MigrateCommon args = { - .listen_uri = "defer", - .start_hook = migrate_hook_start_multifd_tls_x509_default_host, - .end_hook = migrate_hook_end_tls_x509, - .start = { - .caps[MIGRATION_CAPABILITY_MULTIFD] = true, - }, - }; - test_precopy_common(&args); +static void test_multifd_tcp_tls_x509_default_host(char *name, + MigrateCommon *args) +{ + args->listen_uri = "defer"; + args->start_hook = migrate_hook_start_multifd_tls_x509_default_host; + args->end_hook = migrate_hook_end_tls_x509; + + args->start.caps[MIGRATION_CAPABILITY_MULTIFD] = true; + + test_precopy_common(args); } -static void test_multifd_tcp_tls_x509_override_host(void) +static void test_multifd_tcp_tls_x509_override_host(char *name, + MigrateCommon *args) { - MigrateCommon args = { - .listen_uri = "defer", - .start_hook = migrate_hook_start_multifd_tls_x509_override_host, - .end_hook = migrate_hook_end_tls_x509, - .start = { - .caps[MIGRATION_CAPABILITY_MULTIFD] = true, - }, - }; - test_precopy_common(&args); + args->listen_uri = "defer"; + args->start_hook = migrate_hook_start_multifd_tls_x509_override_host; + args->end_hook = migrate_hook_end_tls_x509; + + args->start.caps[MIGRATION_CAPABILITY_MULTIFD] = true; + + test_precopy_common(args); } -static void test_multifd_tcp_tls_x509_mismatch_host(void) +static void test_multifd_tcp_tls_x509_mismatch_host(char *name, + MigrateCommon *args) { /* * This has different behaviour to the non-multifd case. @@ -XXX,XX +XXX,XX @@ static void test_multifd_tcp_tls_x509_mismatch_host(void) * to load migration state, and thus just aborts the migration * without exiting. */ - MigrateCommon args = { - .start = { - .hide_stderr = true, - .caps[MIGRATION_CAPABILITY_MULTIFD] = true, - }, - .listen_uri = "defer", - .start_hook = migrate_hook_start_multifd_tls_x509_mismatch_host, - .end_hook = migrate_hook_end_tls_x509, - .result = MIG_TEST_FAIL, - }; - test_precopy_common(&args); + args->listen_uri = "defer"; + args->start_hook = migrate_hook_start_multifd_tls_x509_mismatch_host; + args->end_hook = migrate_hook_end_tls_x509; + args->result = MIG_TEST_FAIL; + + args->start.hide_stderr = true; + args->start.caps[MIGRATION_CAPABILITY_MULTIFD] = true; + + test_precopy_common(args); } -static void test_multifd_tcp_tls_x509_allow_anon_client(void) +static void test_multifd_tcp_tls_x509_allow_anon_client(char *name, + MigrateCommon *args) { - MigrateCommon args = { - .listen_uri = "defer", - .start_hook = migrate_hook_start_multifd_tls_x509_allow_anon_client, - .end_hook = migrate_hook_end_tls_x509, - .start = { - .caps[MIGRATION_CAPABILITY_MULTIFD] = true, - }, - }; - test_precopy_common(&args); + args->listen_uri = "defer"; + args->start_hook = migrate_hook_start_multifd_tls_x509_allow_anon_client; + args->end_hook = migrate_hook_end_tls_x509; + + args->start.caps[MIGRATION_CAPABILITY_MULTIFD] = true; + + test_precopy_common(args); } -static void test_multifd_tcp_tls_x509_reject_anon_client(void) +static void test_multifd_tcp_tls_x509_reject_anon_client(char *name, + MigrateCommon *args) { - MigrateCommon args = { - .start = { - .hide_stderr = true, - .caps[MIGRATION_CAPABILITY_MULTIFD] = true, - }, - .listen_uri = "defer", - .start_hook = migrate_hook_start_multifd_tls_x509_reject_anon_client, - .end_hook = migrate_hook_end_tls_x509, - .result = MIG_TEST_FAIL, - }; - test_precopy_common(&args); + args->listen_uri = "defer"; + args->start_hook = migrate_hook_start_multifd_tls_x509_reject_anon_client; + args->end_hook = migrate_hook_end_tls_x509; + args->result = MIG_TEST_FAIL; + + args->start.hide_stderr = true; + args->start.caps[MIGRATION_CAPABILITY_MULTIFD] = true; + + test_precopy_common(args); } #endif /* CONFIG_TASN1 */ -- 2.50.1
From: Fabiano Rosas <farosas@suse.de> Pass the "args" parameter to the cancel tests so they can access the config object which will be part of this struct. Signed-off-by: Fabiano Rosas <farosas@suse.de> Link: https://lore.kernel.org/r/20251215220041.12657-27-farosas@suse.de Signed-off-by: Peter Xu <peterx@redhat.com> --- tests/qtest/migration/precopy-tests.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/tests/qtest/migration/precopy-tests.c b/tests/qtest/migration/precopy-tests.c index XXXXXXX..XXXXXXX 100644 --- a/tests/qtest/migration/precopy-tests.c +++ b/tests/qtest/migration/precopy-tests.c @@ -XXX,XX +XXX,XX @@ static void test_multifd_postcopy_tcp_cancel(char *name, MigrateCommon *args) } static void test_cancel_src_after_failed(QTestState *from, QTestState *to, - const char *uri, const char *phase) + const char *uri, const char *phase, + MigrateStart *args) { /* * No migrate_incoming_qmp() at the start to force source into @@ -XXX,XX +XXX,XX @@ static void test_cancel_src_after_failed(QTestState *from, QTestState *to, } static void test_cancel_src_after_cancelled(QTestState *from, QTestState *to, - const char *uri, const char *phase) + const char *uri, const char *phase, + MigrateStart *args) { migrate_incoming_qmp(to, uri, NULL, "{ 'exit-on-error': false }"); @@ -XXX,XX +XXX,XX @@ static void test_cancel_src_after_cancelled(QTestState *from, QTestState *to, } static void test_cancel_src_after_complete(QTestState *from, QTestState *to, - const char *uri, const char *phase) + const char *uri, const char *phase, + MigrateStart *args) { migrate_incoming_qmp(to, uri, NULL, "{ 'exit-on-error': false }"); @@ -XXX,XX +XXX,XX @@ static void test_cancel_src_after_complete(QTestState *from, QTestState *to, } static void test_cancel_src_after_none(QTestState *from, QTestState *to, - const char *uri, const char *phase) + const char *uri, const char *phase, + MigrateStart *args) { /* * Test that cancelling without a migration happening does not @@ -XXX,XX +XXX,XX @@ static void test_cancel_src_after_none(QTestState *from, QTestState *to, } static void test_cancel_src_pre_switchover(QTestState *from, QTestState *to, - const char *uri, const char *phase) + const char *uri, const char *phase, + MigrateStart *args) { migrate_set_capability(from, "pause-before-switchover", true); migrate_set_capability(to, "pause-before-switchover", true); @@ -XXX,XX +XXX,XX @@ static void test_cancel_src_after_status(char *test_path, MigrateCommon *args) if (g_str_equal(phase, "cancelling") || g_str_equal(phase, "cancelled")) { - test_cancel_src_after_cancelled(from, to, uri, phase); + test_cancel_src_after_cancelled(from, to, uri, phase, &args->start); } else if (g_str_equal(phase, "completed")) { - test_cancel_src_after_complete(from, to, uri, phase); + test_cancel_src_after_complete(from, to, uri, phase, &args->start); } else if (g_str_equal(phase, "failed")) { - test_cancel_src_after_failed(from, to, uri, phase); + test_cancel_src_after_failed(from, to, uri, phase, &args->start); } else if (g_str_equal(phase, "none")) { - test_cancel_src_after_none(from, to, uri, phase); + test_cancel_src_after_none(from, to, uri, phase, &args->start); } else { /* any state that comes before pre-switchover */ - test_cancel_src_pre_switchover(from, to, uri, phase); + test_cancel_src_pre_switchover(from, to, uri, phase, &args->start); } migrate_end(from, to, false); -- 2.50.1
From: Chuang Xu <xuchuangxclwt@bytedance.com> In our long-term experience in Bytedance, we've found that under the same load, live migration of larger VMs with more devices is often more difficult to converge (requiring a larger downtime limit). Through some testing and calculations, we conclude that bitmap sync time affects the calculation of live migration bandwidth. When the addresses processed are not aligned, a large number of clear_dirty ioctl occur (e.g. a 4MB misaligned memory can generate 2048 clear_dirty ioctls from two different memory_listener), which increases the time required for bitmap_sync and makes it more difficult for dirty pages to converge. For a 64C256G vm with 8 vhost-user-net(32 queue per nic) and 16 vhost-user-blk(4 queue per blk), the sync time is as high as *73ms* (tested with 10GBps dirty rate, the sync time increases as the dirty page rate increases), Here are each part of the sync time: - sync from kvm to ram_list: 2.5ms - vhost_log_sync:3ms - sync aligned memory from ram_list to RAMBlock: 5ms - sync misaligned memory from ram_list to RAMBlock: 61ms Attempt to merge those fragmented clear_dirty ioctls, then syncing misaligned memory from ram_list to RAMBlock takes only about 1ms, and the total sync time is only *12ms*. Signed-off-by: Chuang Xu <xuchuangxclwt@bytedance.com> Reviewed-by: Fabiano Rosas <farosas@suse.de> Link: https://lore.kernel.org/r/20251218114220.83354-1-xuchuangxclwt@bytedance.com [peterx: drop var "offset" in physical_memory_sync_dirty_bitmap] Signed-off-by: Peter Xu <peterx@redhat.com> --- include/system/physmem.h | 7 +++--- accel/tcg/cputlb.c | 5 +++-- migration/ram.c | 19 +++++----------- system/memory.c | 2 +- system/physmem.c | 48 +++++++++++++++++++++++++++------------- 5 files changed, 46 insertions(+), 35 deletions(-) diff --git a/include/system/physmem.h b/include/system/physmem.h index XXXXXXX..XXXXXXX 100644 --- a/include/system/physmem.h +++ b/include/system/physmem.h @@ -XXX,XX +XXX,XX @@ uint64_t physical_memory_set_dirty_lebitmap(unsigned long *bitmap, void physical_memory_dirty_bits_cleared(ram_addr_t start, ram_addr_t length); -bool physical_memory_test_and_clear_dirty(ram_addr_t start, - ram_addr_t length, - unsigned client); +uint64_t physical_memory_test_and_clear_dirty(ram_addr_t start, + ram_addr_t length, + unsigned client, + unsigned long *bmap); DirtyBitmapSnapshot * physical_memory_snapshot_and_clear_dirty(MemoryRegion *mr, hwaddr offset, diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index XXXXXXX..XXXXXXX 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -XXX,XX +XXX,XX @@ void tlb_flush_page_bits_by_mmuidx_all_cpus_synced(CPUState *src_cpu, void tlb_protect_code(ram_addr_t ram_addr) { physical_memory_test_and_clear_dirty(ram_addr & TARGET_PAGE_MASK, - TARGET_PAGE_SIZE, - DIRTY_MEMORY_CODE); + TARGET_PAGE_SIZE, + DIRTY_MEMORY_CODE, + NULL); } /* update the TLB so that writes in physical page 'phys_addr' are no longer diff --git a/migration/ram.c b/migration/ram.c index XXXXXXX..XXXXXXX 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -XXX,XX +XXX,XX @@ static uint64_t physical_memory_sync_dirty_bitmap(RAMBlock *rb, ram_addr_t start, ram_addr_t length) { - ram_addr_t addr; unsigned long word = BIT_WORD((start + rb->offset) >> TARGET_PAGE_BITS); uint64_t num_dirty = 0; unsigned long *dest = rb->bmap; @@ -XXX,XX +XXX,XX @@ static uint64_t physical_memory_sync_dirty_bitmap(RAMBlock *rb, memory_region_clear_dirty_bitmap(rb->mr, start, length); } } else { - ram_addr_t offset = rb->offset; - - for (addr = 0; addr < length; addr += TARGET_PAGE_SIZE) { - if (physical_memory_test_and_clear_dirty( - start + addr + offset, - TARGET_PAGE_SIZE, - DIRTY_MEMORY_MIGRATION)) { - long k = (start + addr) >> TARGET_PAGE_BITS; - if (!test_and_set_bit(k, dest)) { - num_dirty++; - } - } - } + num_dirty = physical_memory_test_and_clear_dirty( + start + rb->offset, + length, + DIRTY_MEMORY_MIGRATION, + dest); } return num_dirty; diff --git a/system/memory.c b/system/memory.c index XXXXXXX..XXXXXXX 100644 --- a/system/memory.c +++ b/system/memory.c @@ -XXX,XX +XXX,XX @@ void memory_region_reset_dirty(MemoryRegion *mr, hwaddr addr, { assert(mr->ram_block); physical_memory_test_and_clear_dirty( - memory_region_get_ram_addr(mr) + addr, size, client); + memory_region_get_ram_addr(mr) + addr, size, client, NULL); } int memory_region_get_fd(MemoryRegion *mr) diff --git a/system/physmem.c b/system/physmem.c index XXXXXXX..XXXXXXX 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -XXX,XX +XXX,XX @@ void physical_memory_set_dirty_range(ram_addr_t start, ram_addr_t length, } } -/* Note: start and end must be within the same ram block. */ -bool physical_memory_test_and_clear_dirty(ram_addr_t start, +/* + * Note: start and end must be within the same ram block. + * + * @bmap usage: + * - When @bmap is provided, set bits for dirty pages, but + * only count those pages if the bit wasn't already set in @bmap. + * - When @bmap is NULL, count all dirty pages in the range. + * + * @return: + * - Number of dirty guest pages found within [start, start + length). + */ +uint64_t physical_memory_test_and_clear_dirty(ram_addr_t start, ram_addr_t length, - unsigned client) + unsigned client, + unsigned long *bmap) { DirtyMemoryBlocks *blocks; unsigned long end, page, start_page; - bool dirty = false; + uint64_t num_dirty = 0; RAMBlock *ramblock; uint64_t mr_offset, mr_size; if (length == 0) { - return false; + return 0; } end = TARGET_PAGE_ALIGN(start + length) >> TARGET_PAGE_BITS; @@ -XXX,XX +XXX,XX @@ bool physical_memory_test_and_clear_dirty(ram_addr_t start, while (page < end) { unsigned long idx = page / DIRTY_MEMORY_BLOCK_SIZE; unsigned long offset = page % DIRTY_MEMORY_BLOCK_SIZE; - unsigned long num = MIN(end - page, - DIRTY_MEMORY_BLOCK_SIZE - offset); - dirty |= bitmap_test_and_clear_atomic(blocks->blocks[idx], - offset, num); - page += num; + if (bitmap_test_and_clear_atomic(blocks->blocks[idx], offset, 1)) { + if (bmap) { + unsigned long k = page - (ramblock->offset >> TARGET_PAGE_BITS); + if (!test_and_set_bit(k, bmap)) { + num_dirty++; + } + } else { + num_dirty++; + } + } + + page++; } mr_offset = (ram_addr_t)(start_page << TARGET_PAGE_BITS) - ramblock->offset; @@ -XXX,XX +XXX,XX @@ bool physical_memory_test_and_clear_dirty(ram_addr_t start, memory_region_clear_dirty_bitmap(ramblock->mr, mr_offset, mr_size); } - if (dirty) { + if (num_dirty) { physical_memory_dirty_bits_cleared(start, length); } - return dirty; + return num_dirty; } static void physical_memory_clear_dirty_range(ram_addr_t addr, ram_addr_t length) { - physical_memory_test_and_clear_dirty(addr, length, DIRTY_MEMORY_MIGRATION); - physical_memory_test_and_clear_dirty(addr, length, DIRTY_MEMORY_VGA); - physical_memory_test_and_clear_dirty(addr, length, DIRTY_MEMORY_CODE); + physical_memory_test_and_clear_dirty(addr, length, DIRTY_MEMORY_MIGRATION, NULL); + physical_memory_test_and_clear_dirty(addr, length, DIRTY_MEMORY_VGA, NULL); + physical_memory_test_and_clear_dirty(addr, length, DIRTY_MEMORY_CODE, NULL); } DirtyBitmapSnapshot *physical_memory_snapshot_and_clear_dirty -- 2.50.1
From: "David Hildenbrand (Red Hat)" <david@kernel.org> I don't have a lot of capacity to do any maintanance (or even review) of "Memory API" lately, so remove myself. Fortunately we still do have two other maintainers and one reviewer :) Cc: Paolo Bonzini <pbonzini@redhat.com> Cc: Peter Xu <peterx@redhat.com> Cc: Philippe Mathieu-Daudé <philmd@linaro.org> Signed-off-by: David Hildenbrand (Red Hat) <david@kernel.org> Link: https://lore.kernel.org/r/20251222141438.409218-1-david@kernel.org Signed-off-by: Peter Xu <peterx@redhat.com> --- MAINTAINERS | 1 - 1 file changed, 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index XXXXXXX..XXXXXXX 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -XXX,XX +XXX,XX @@ T: git https://gitlab.com/stsquad/qemu gdbstub/next Memory API M: Paolo Bonzini <pbonzini@redhat.com> M: Peter Xu <peterx@redhat.com> -M: David Hildenbrand <david@kernel.org> R: Philippe Mathieu-Daudé <philmd@linaro.org> S: Supported F: include/system/ioport.h -- 2.50.1
The following changes since commit ac0cc20ad2fe0b8df2e5d9458e90a095ac711ab1: Merge tag 'for-upstream' of https://gitlab.com/bonzini/qemu into staging (2026-05-01 14:41:49 -0400) are available in the Git repository at: https://gitlab.com/peterx/qemu.git tags/next-pull-request for you to fetch changes up to 348c67cb16ecb479994b0c957699f906bd40a6c2: tests/qtest/migration: Fix A-B file build (2026-05-05 12:35:25 -0400) ---------------------------------------------------------------- Migration and mem pull request - Fabiano's fix on migrate_set_parameter crash with multifd & zerocopy - Pranav's fix on postcopy stucking at device state when ack lost - Samuel's new migration parameter x-rdma-chunk-size for RDMA - PeterX's vfio/migration series to report remaining data and fix downtime calc - PeterM's MemoryRegionOps .impl cleanup series - Fabiano's fix to build a-b migration bootfiles for all archs ---------------------------------------------------------------- CJ Chen (2): hw/riscv: iommu-trap: remove .impl.unaligned = true system/memory: assert on invalid MemoryRegionOps .unaligned combo Fabiano Rosas (2): migration: Use QAPI_CLONE_MEMBERS in migrate_params_test_apply tests/qtest/migration: Fix A-B file build Peter Maydell (2): hw/npcm7xx_fiu: Specify .impl for npcm7xx_fiu_flash_ops hw/xtensa/mx_pic: Specify xtensa_mx_pic_ops .impl settings Peter Xu (15): migration: Fix low possibility downtime violation migration/qapi: Rename MigrationStats to MigrationRAMStats vfio/migration: Cache stop size in VFIOMigration migration/treewide: Merge @state_pending_{exact|estimate} APIs migration: Use the new save_query_pending() API directly migration: Introduce stopcopy_bytes in save_query_pending() vfio/migration: Fix incorrect reporting for VFIO pending data migration: Move iteration counter out of RAM migration: Introduce a helper to return switchover bw estimate migration: Calculate expected downtime on demand migration: Fix calculation of expected_downtime to take VFIO info migration: Remember total dirty bytes in mig_stats migration/qapi: Introduce system-wide "remaining" reports migration/qapi: Update unit for avail-switchover-bandwidth vfio/migration: Add tracepoints for precopy/stopcopy query ioctls Pranav Tyagi (1): migration: Fix blocking in POSTCOPY_DEVICE during package load Samuel Zhang (1): migration/rdma: add x-rdma-chunk-size parameter docs/about/removed-features.rst | 2 +- docs/devel/migration/main.rst | 9 +- docs/devel/migration/vfio.rst | 9 +- qapi/migration.json | 45 +- hw/vfio/vfio-migration-internal.h | 8 + include/migration/register.h | 59 +- migration/migration-stats.h | 20 +- migration/migration.h | 3 +- migration/options.h | 1 + migration/savevm.h | 7 +- tests/qtest/migration/aarch64/a-b-kernel.h | 7 +- tests/qtest/migration/bootfile.h | 11 + tests/qtest/migration/i386/a-b-bootblock.h | 8 +- tests/qtest/migration/ppc64/a-b-kernel.h | 8 +- tests/qtest/migration/s390x/a-b-bios.h | 611 +++++++++++++-------- hw/riscv/riscv-iommu.c | 1 - hw/s390x/s390-stattrib.c | 9 +- hw/ssi/npcm7xx_fiu.c | 5 + hw/vfio/migration.c | 125 +++-- hw/xtensa/mx_pic.c | 27 + migration/block-dirty-bitmap.c | 10 +- migration/migration-hmp-cmds.c | 16 + migration/migration.c | 225 +++++--- migration/options.c | 59 +- migration/ram.c | 40 +- migration/rdma.c | 30 +- migration/savevm.c | 42 +- system/memory.c | 1 + tests/qtest/migration/s390x/a-b-bios.c | 66 ++- hw/vfio/trace-events | 5 +- migration/trace-events | 3 +- tests/qtest/migration/Makefile | 11 +- tests/qtest/migration/aarch64/Makefile | 4 +- tests/qtest/migration/aarch64/a-b-kernel.S | 2 +- tests/qtest/migration/i386/Makefile | 4 +- tests/qtest/migration/i386/a-b-bootblock.S | 2 +- tests/qtest/migration/ppc64/Makefile | 6 +- tests/qtest/migration/ppc64/a-b-kernel.S | 2 +- tests/qtest/migration/s390x/Makefile | 8 +- 39 files changed, 957 insertions(+), 554 deletions(-) -- 2.53.0
From: Pranav Tyagi <prtyagi@redhat.com> The package_loaded event is not set in case MIG_RP_MSG_PONG does not arrive on the source from the destination in the return path thread. The migration thread would then be blocked waiting for package_loaded event indefinitely in POSTCOPY_DEVICE state. Where as, in such a condition the source VM can safely resume as the destination has not yet started. The pong message can get lost in case of a network failure or destination crash before sending the pong. This patch removes the package_loaded event and uses rp_sem, instead of kicking multiple events. The error is detected in case of network failure or destination crash and rp_sem is set in the out path of the return path thread. This will kick the migration thread out from a condition of indefinitely waiting for rp_sem. The migration thread then fails early and breaks from the migration loop to resume the vm on the source side. Fixes: 7b842fe354c6 ("migration: Introduce POSTCOPY_DEVICE state") Signed-off-by: Pranav Tyagi <prtyagi@redhat.com> Reviewed-by: Juraj Marcin <jmarcin@redhat.com> Reviewed-by: Peter Xu <peterx@redhat.com> Link: https://lore.kernel.org/r/20260423094438.43556-1-prtyagi@redhat.com Signed-off-by: Peter Xu <peterx@redhat.com> --- migration/migration.h | 1 - migration/migration.c | 48 ++++++++++++++++++++++++++++--------------- 2 files changed, 31 insertions(+), 18 deletions(-) diff --git a/migration/migration.h b/migration/migration.h index XXXXXXX..XXXXXXX 100644 --- a/migration/migration.h +++ b/migration/migration.h @@ -XXX,XX +XXX,XX @@ struct MigrationState { bool rdma_migration; bool postcopy_package_loaded; - QemuEvent postcopy_package_loaded_event; GSource *hup_source; diff --git a/migration/migration.c b/migration/migration.c index XXXXXXX..XXXXXXX 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -XXX,XX +XXX,XX @@ int migrate_init(MigrationState *s, Error **errp) migration_reset_vfio_bytes_transferred(); s->postcopy_package_loaded = false; - qemu_event_reset(&s->postcopy_package_loaded_event); return 0; } @@ -XXX,XX +XXX,XX @@ static void *source_return_path_thread(void *opaque) if (tmp32 == QEMU_VM_PING_PACKAGED_LOADED) { trace_source_return_path_thread_postcopy_package_loaded(); ms->postcopy_package_loaded = true; - qemu_event_set(&ms->postcopy_package_loaded_event); + migration_rp_kick(ms); } break; @@ -XXX,XX +XXX,XX @@ out: trace_source_return_path_thread_bad_end(); } - if (ms->state == MIGRATION_STATUS_POSTCOPY_RECOVER) { + if (ms->state == MIGRATION_STATUS_POSTCOPY_RECOVER || + ms->state == MIGRATION_STATUS_POSTCOPY_DEVICE) { /* - * this will be extremely unlikely: that we got yet another network - * issue during recovering of the 1st network failure.. during this - * period the main migration thread can be waiting on rp_sem for - * this thread to sync with the other side. + * The migration thread can get stuck waiting for rp_sem if the + * return path fails to sync with the destination. This handles + * two specific cases: * - * When this happens, explicitly kick the migration thread out of - * RECOVER stage and back to PAUSED, so the admin can try - * everything again. + * POSTCOPY_RECOVER: A failure occurs during a recovery attempt. + * We kick the migration thread back to PAUSED so the admin can + * retry. + * + * POSTCOPY_DEVICE: The MIG_RP_MSG_PONG is lost due to a + * network failure or destination crash. We kick the migration + * thread out of its wait so it can fail the migration and safely + * resume the VM on the source. */ migration_rp_kick(ms); } @@ -XXX,XX +XXX,XX @@ static MigIterateState migration_iteration_run(MigrationState *s) if (s->state == MIGRATION_STATUS_POSTCOPY_DEVICE && (s->postcopy_package_loaded || complete_ready)) { /* - * If package has been loaded, the event is set and we will - * immediatelly transition to POSTCOPY_ACTIVE. If we are ready for - * completion, we need to wait for destination to load the postcopy - * package before actually completing. + * We will immediately transition to POSTCOPY_ACTIVE. + * If we are ready for completion, we need to wait for + * destination to load the postcopy package before actually + * completing. */ - qemu_event_wait(&s->postcopy_package_loaded_event); + while (!s->postcopy_package_loaded) { + if (migration_rp_wait(s)) { + /* + * Error happened. Migration thread was stuck waiting in + * POSTCOPY_DEVICE for rp_sem which was never set. + */ + migrate_set_state(&s->state, + MIGRATION_STATUS_POSTCOPY_DEVICE, + MIGRATION_STATUS_FAILING); + return MIG_ITERATE_BREAK; + } + } + /* Acknowledgement received from the destination */ migrate_set_state(&s->state, MIGRATION_STATUS_POSTCOPY_DEVICE, MIGRATION_STATUS_POSTCOPY_ACTIVE); } @@ -XXX,XX +XXX,XX @@ static void migration_instance_finalize(Object *obj) qemu_sem_destroy(&ms->rp_state.rp_pong_acks); qemu_sem_destroy(&ms->postcopy_qemufile_src_sem); error_free(ms->error); - qemu_event_destroy(&ms->postcopy_package_loaded_event); } static void migration_instance_init(Object *obj) @@ -XXX,XX +XXX,XX @@ static void migration_instance_init(Object *obj) qemu_sem_init(&ms->wait_unplug_sem, 0); qemu_sem_init(&ms->postcopy_qemufile_src_sem, 0); qemu_mutex_init(&ms->qemu_file_lock); - qemu_event_init(&ms->postcopy_package_loaded_event, 0); } /* -- 2.53.0
From: Fabiano Rosas <farosas@suse.de> Use QAPI_CLONE_MEMBERS instead of making an assignment. The QAPI method makes the handling of the TLS strings more intuitive because it clones them as well. This also fixes a segfault when a NULL TLS option is accessed as part of a validation check for another option (e.g. in the zero-copy + multifd compression case). Details follow: Currently, after copying s->parameters to the temporary MigrationParameters object before migrate_params_check(), the references in temporary object to the TLS options are dropped, either because: a) the user set a new option, in which case that's fine as s->parameters still holds the reference to the old memory or, b) the user did not set a new option, in which case keeping the references in the temporary object would later cause them to be freed along with it, leading to double-free when s->parameters is also freed later on. In this second case, it was overlooked that the TLS options can be accessed already during migrate_params_check() as part of validation of another option. Those pointers should not have been cleared. Using QAPI_CLONE_MEMBERS fixes the issue because the temporary object is not stealing a reference from s->parameters anymore. Cc: qemu-stable <qemu-stable@nongnu.org> Fixes: aed97f0563 ("migration: Normalize tls arguments") Reported-by: Maciej S. Szmigiero <mail@maciej.szmigiero.name> Link: https://lore.kernel.org/r/a65a1049-9f19-460a-8e27-a62bb30d2727@maciej.szmigiero.name Reviewed-by: Peter Xu <peterx@redhat.com> Signed-off-by: Fabiano Rosas <farosas@suse.de> Tested-by: Maciej S. Szmigiero <maciej.szmigiero@oracle.com> Link: https://lore.kernel.org/r/20260414223718.23965-1-farosas@suse.de Signed-off-by: Peter Xu <peterx@redhat.com> --- migration/options.c | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/migration/options.c b/migration/options.c index XXXXXXX..XXXXXXX 100644 --- a/migration/options.c +++ b/migration/options.c @@ -XXX,XX +XXX,XX @@ bool migrate_params_check(MigrationParameters *params, Error **errp) static void migrate_params_test_apply(MigrationParameters *params, MigrationParameters *dest) { - *dest = migrate_get_current()->parameters; + MigrationState *s = migrate_get_current(); - /* TODO use QAPI_CLONE() instead of duplicating it inline */ + QAPI_CLONE_MEMBERS(MigrationParameters, dest, &s->parameters); if (params->has_throttle_trigger_threshold) { dest->throttle_trigger_threshold = params->throttle_trigger_threshold; @@ -XXX,XX +XXX,XX @@ static void migrate_params_test_apply(MigrationParameters *params, } if (params->tls_creds) { + qapi_free_StrOrNull(dest->tls_creds); dest->tls_creds = QAPI_CLONE(StrOrNull, params->tls_creds); - } else { - /* clear the reference, it's owned by s->parameters */ - dest->tls_creds = NULL; } if (params->tls_hostname) { + qapi_free_StrOrNull(dest->tls_hostname); dest->tls_hostname = QAPI_CLONE(StrOrNull, params->tls_hostname); - } else { - /* clear the reference, it's owned by s->parameters */ - dest->tls_hostname = NULL; } if (params->tls_authz) { + qapi_free_StrOrNull(dest->tls_authz); dest->tls_authz = QAPI_CLONE(StrOrNull, params->tls_authz); - } else { - /* clear the reference, it's owned by s->parameters */ - dest->tls_authz = NULL; } if (params->has_max_bandwidth) { @@ -XXX,XX +XXX,XX @@ static void migrate_params_test_apply(MigrationParameters *params, } if (params->has_block_bitmap_mapping) { - dest->has_block_bitmap_mapping = true; - dest->block_bitmap_mapping = params->block_bitmap_mapping; + qapi_free_BitmapMigrationNodeAliasList(dest->block_bitmap_mapping); + dest->block_bitmap_mapping = QAPI_CLONE(BitmapMigrationNodeAliasList, + params->block_bitmap_mapping); } if (params->has_x_vcpu_dirty_limit_period) { @@ -XXX,XX +XXX,XX @@ static void migrate_params_test_apply(MigrationParameters *params, } if (params->has_cpr_exec_command) { - dest->cpr_exec_command = params->cpr_exec_command; + qapi_free_strList(dest->cpr_exec_command); + dest->cpr_exec_command = QAPI_CLONE(strList, params->cpr_exec_command); } } @@ -XXX,XX +XXX,XX @@ void qmp_migrate_set_parameters(MigrationParameters *params, Error **errp) } migrate_tls_opts_free(&tmp); + qapi_free_BitmapMigrationNodeAliasList(tmp.block_bitmap_mapping); + qapi_free_strList(tmp.cpr_exec_command); } -- 2.53.0
From: Samuel Zhang <guoqing.zhang@amd.com> The default 1MB RDMA chunk size causes slow live migration because each chunk triggers a write_flush (ibv_post_send). For 8GB RAM, 1MB chunk size produces ~15000 flushes vs ~3700 with 1024MB chunk size. Add x-rdma-chunk-size parameter to configure the RDMA chunk size for faster migration. Usage: `migrate_set_parameter x-rdma-chunk-size 1024M` Performance with RDMA live migration of 8GB RAM VM: | x-rdma-chunk-size (B) | time (s) | throughput (MB/s) | |-----------------------|----------|-------------------| | 1M (default) | 37.915 | 1,007 | | 32M | 17.880 | 2,260 | | 1024M | 4.368 | 17,529 | Signed-off-by: Samuel Zhang <guoqing.zhang@amd.com> Acked-by: Markus Armbruster <armbru@redhat.com> Acked-by: Li Zhijian <lizhijian@fujitsu.com> Tested-by: Li Zhijian <lizhijian@fujitsu.com> Acked-by: Fabiano Rosas <farosas@suse.de> Acked-by: Peter Xu <peterx@redhat.com> Link: https://lore.kernel.org/r/20260427031401.3895523-1-guoqing.zhang@amd.com Signed-off-by: Peter Xu <peterx@redhat.com> --- qapi/migration.json | 13 +++++++++++-- migration/options.h | 1 + migration/migration-hmp-cmds.c | 11 +++++++++++ migration/options.c | 33 ++++++++++++++++++++++++++++++++- migration/rdma.c | 30 ++++++++++++++++-------------- 5 files changed, 71 insertions(+), 17 deletions(-) diff --git a/qapi/migration.json b/qapi/migration.json index XXXXXXX..XXXXXXX 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -XXX,XX +XXX,XX @@ # # Features: # -# @unstable: Members @x-checkpoint-delay and +# @unstable: Members @x-checkpoint-delay, @x-rdma-chunk-size, and # @x-vcpu-dirty-limit-period are experimental. # # Since: 2.4 @@ -XXX,XX +XXX,XX @@ 'mode', 'zero-page-detection', 'direct-io', + { 'name': 'x-rdma-chunk-size', 'features': [ 'unstable' ] }, 'cpr-exec-command'] } ## @@ -XXX,XX +XXX,XX @@ # is @cpr-exec. The first list element is the program's filename, # the remainder its arguments. (Since 10.2) # +# @x-rdma-chunk-size: RDMA memory registration chunk size in bytes. +# Default is 1MiB. Must be a power of 2 in the range +# [1MiB, 1024MiB]. Only applies when migrating via RDMA. +# Must be set to the same value on both source and destination +# before migration starts. (Since 11.1) +# # Features: # -# @unstable: Members @x-checkpoint-delay and +# @unstable: Members @x-checkpoint-delay, @x-rdma-chunk-size, and # @x-vcpu-dirty-limit-period are experimental. # # Since: 2.4 @@ -XXX,XX +XXX,XX @@ '*mode': 'MigMode', '*zero-page-detection': 'ZeroPageDetection', '*direct-io': 'bool', + '*x-rdma-chunk-size': { 'type': 'uint64', + 'features': [ 'unstable' ] }, '*cpr-exec-command': [ 'str' ]} } ## diff --git a/migration/options.h b/migration/options.h index XXXXXXX..XXXXXXX 100644 --- a/migration/options.h +++ b/migration/options.h @@ -XXX,XX +XXX,XX @@ const char *migrate_tls_creds(void); const char *migrate_tls_hostname(void); uint64_t migrate_xbzrle_cache_size(void); ZeroPageDetection migrate_zero_page_detection(void); +uint64_t migrate_rdma_chunk_size(void); /* parameters helpers */ diff --git a/migration/migration-hmp-cmds.c b/migration/migration-hmp-cmds.c index XXXXXXX..XXXXXXX 100644 --- a/migration/migration-hmp-cmds.c +++ b/migration/migration-hmp-cmds.c @@ -XXX,XX +XXX,XX @@ void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict) params->direct_io ? "on" : "off"); } + if (params->has_x_rdma_chunk_size) { + monitor_printf(mon, "%s: %" PRIu64 " bytes\n", + MigrationParameter_str( + MIGRATION_PARAMETER_X_RDMA_CHUNK_SIZE), + params->x_rdma_chunk_size); + } + assert(params->has_cpr_exec_command); monitor_print_cpr_exec_command(mon, params->cpr_exec_command); } @@ -XXX,XX +XXX,XX @@ void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict) p->has_direct_io = true; visit_type_bool(v, param, &p->direct_io, &err); break; + case MIGRATION_PARAMETER_X_RDMA_CHUNK_SIZE: + p->has_x_rdma_chunk_size = true; + visit_type_size(v, param, &p->x_rdma_chunk_size, &err); + break; case MIGRATION_PARAMETER_CPR_EXEC_COMMAND: { /* * NOTE: g_autofree will only auto g_free() the strv array when diff --git a/migration/options.c b/migration/options.c index XXXXXXX..XXXXXXX 100644 --- a/migration/options.c +++ b/migration/options.c @@ -XXX,XX +XXX,XX @@ #include "qemu/osdep.h" #include "qemu/error-report.h" +#include "qemu/units.h" #include "exec/target_page.h" #include "qapi/clone-visitor.h" #include "qapi/error.h" @@ -XXX,XX +XXX,XX @@ const PropertyInfo qdev_prop_StrOrNull; #define DEFAULT_MIGRATE_VCPU_DIRTY_LIMIT_PERIOD 1000 /* milliseconds */ #define DEFAULT_MIGRATE_VCPU_DIRTY_LIMIT 1 /* MB/s */ +#define DEFAULT_MIGRATE_X_RDMA_CHUNK_SIZE MiB const Property migration_properties[] = { DEFINE_PROP_BOOL("store-global-state", MigrationState, @@ -XXX,XX +XXX,XX @@ const Property migration_properties[] = { DEFINE_PROP_ZERO_PAGE_DETECTION("zero-page-detection", MigrationState, parameters.zero_page_detection, ZERO_PAGE_DETECTION_MULTIFD), + DEFINE_PROP_UINT64("x-rdma-chunk-size", MigrationState, + parameters.x_rdma_chunk_size, + DEFAULT_MIGRATE_X_RDMA_CHUNK_SIZE), /* Migration capabilities */ DEFINE_PROP_MIG_CAP("x-xbzrle", MIGRATION_CAPABILITY_XBZRLE), @@ -XXX,XX +XXX,XX @@ ZeroPageDetection migrate_zero_page_detection(void) return s->parameters.zero_page_detection; } +uint64_t migrate_rdma_chunk_size(void) +{ + MigrationState *s = migrate_get_current(); + uint64_t size = s->parameters.x_rdma_chunk_size; + + assert(MiB <= size && size <= GiB && is_power_of_2(size)); + return size; +} + /* parameters helpers */ AnnounceParameters *migrate_announce_params(void) @@ -XXX,XX +XXX,XX @@ static void migrate_mark_all_params_present(MigrationParameters *p) &p->has_announce_step, &p->has_block_bitmap_mapping, &p->has_x_vcpu_dirty_limit_period, &p->has_vcpu_dirty_limit, &p->has_mode, &p->has_zero_page_detection, &p->has_direct_io, - &p->has_cpr_exec_command, + &p->has_x_rdma_chunk_size, &p->has_cpr_exec_command, }; len = ARRAY_SIZE(has_fields); @@ -XXX,XX +XXX,XX @@ bool migrate_params_check(MigrationParameters *params, Error **errp) return false; } + if (params->has_x_rdma_chunk_size && + (params->x_rdma_chunk_size < MiB || + params->x_rdma_chunk_size > GiB || + !is_power_of_2(params->x_rdma_chunk_size))) { + error_setg(errp, "Option x_rdma_chunk_size expects " + "a power of 2 in the range 1MiB to 1024MiB"); + return false; + } + return true; } @@ -XXX,XX +XXX,XX @@ static void migrate_params_test_apply(MigrationParameters *params, dest->direct_io = params->direct_io; } + if (params->has_x_rdma_chunk_size) { + dest->x_rdma_chunk_size = params->x_rdma_chunk_size; + } + if (params->has_cpr_exec_command) { qapi_free_strList(dest->cpr_exec_command); dest->cpr_exec_command = QAPI_CLONE(strList, params->cpr_exec_command); @@ -XXX,XX +XXX,XX @@ static void migrate_params_apply(MigrationParameters *params) s->parameters.direct_io = params->direct_io; } + if (params->has_x_rdma_chunk_size) { + s->parameters.x_rdma_chunk_size = params->x_rdma_chunk_size; + } + if (params->has_cpr_exec_command) { qapi_free_strList(s->parameters.cpr_exec_command); s->parameters.cpr_exec_command = diff --git a/migration/rdma.c b/migration/rdma.c index XXXXXXX..XXXXXXX 100644 --- a/migration/rdma.c +++ b/migration/rdma.c @@ -XXX,XX +XXX,XX @@ #define RDMA_RESOLVE_TIMEOUT_MS 10000 /* Do not merge data if larger than this. */ -#define RDMA_MERGE_MAX (2 * 1024 * 1024) -#define RDMA_SIGNALED_SEND_MAX (RDMA_MERGE_MAX / 4096) +static inline uint64_t rdma_merge_max(void) +{ + return migrate_rdma_chunk_size() * 2; +} -#define RDMA_REG_CHUNK_SHIFT 20 /* 1 MB */ +#define RDMA_SIGNALED_SEND_MAX 512 /* * This is only for non-live state being migrated. @@ -XXX,XX +XXX,XX @@ static int qemu_rdma_exchange_send(RDMAContext *rdma, RDMAControlHeader *head, static inline uint64_t ram_chunk_index(const uint8_t *start, const uint8_t *host) { - return ((uintptr_t) host - (uintptr_t) start) >> RDMA_REG_CHUNK_SHIFT; + return ((uintptr_t) host - (uintptr_t) start) / migrate_rdma_chunk_size(); } static inline uint8_t *ram_chunk_start(const RDMALocalBlock *rdma_ram_block, uint64_t i) { return (uint8_t *)(uintptr_t)(rdma_ram_block->local_host_addr + - (i << RDMA_REG_CHUNK_SHIFT)); + (i * migrate_rdma_chunk_size())); } static inline uint8_t *ram_chunk_end(const RDMALocalBlock *rdma_ram_block, uint64_t i) { uint8_t *result = ram_chunk_start(rdma_ram_block, i) + - (1UL << RDMA_REG_CHUNK_SHIFT); + migrate_rdma_chunk_size(); if (result > (rdma_ram_block->local_host_addr + rdma_ram_block->length)) { result = rdma_ram_block->local_host_addr + rdma_ram_block->length; @@ -XXX,XX +XXX,XX @@ static int qemu_rdma_write_one(RDMAContext *rdma, struct ibv_send_wr *bad_wr; int reg_result_idx, ret, count = 0; uint64_t chunk, chunks; + uint64_t chunk_size = migrate_rdma_chunk_size(); uint8_t *chunk_start, *chunk_end; RDMALocalBlock *block = &(rdma->local_ram_blocks.block[current_index]); RDMARegister reg; @@ -XXX,XX +XXX,XX @@ retry: chunk_start = ram_chunk_start(block, chunk); if (block->is_ram_block) { - chunks = length / (1UL << RDMA_REG_CHUNK_SHIFT); + chunks = length / chunk_size; - if (chunks && ((length % (1UL << RDMA_REG_CHUNK_SHIFT)) == 0)) { + if (chunks && ((length % chunk_size) == 0)) { chunks--; } } else { - chunks = block->length / (1UL << RDMA_REG_CHUNK_SHIFT); + chunks = block->length / chunk_size; - if (chunks && ((block->length % (1UL << RDMA_REG_CHUNK_SHIFT)) == 0)) { + if (chunks && ((block->length % chunk_size) == 0)) { chunks--; } } trace_qemu_rdma_write_one_top(chunks + 1, - (chunks + 1) * - (1UL << RDMA_REG_CHUNK_SHIFT) / 1024 / 1024); + (chunks + 1) * chunk_size / 1024 / 1024); chunk_end = ram_chunk_end(block, chunk + chunks); @@ -XXX,XX +XXX,XX @@ static int qemu_rdma_write(RDMAContext *rdma, rdma->current_length += len; /* flush it if buffer is too large */ - if (rdma->current_length >= RDMA_MERGE_MAX) { + if (rdma->current_length >= rdma_merge_max()) { return qemu_rdma_write_flush(rdma, errp); } @@ -XXX,XX +XXX,XX @@ int rdma_registration_handle(QEMUFile *f) } else { chunk = reg->key.chunk; host_addr = block->local_host_addr + - (reg->key.chunk * (1UL << RDMA_REG_CHUNK_SHIFT)); + (reg->key.chunk * migrate_rdma_chunk_size()); /* Check for particularly bad chunk value */ if (host_addr < (void *)block->local_host_addr) { error_report("rdma: bad chunk for block %s" -- 2.53.0
When QEMU queried the estimated version of pending data and thinks it's ready to converge, it'll send another accurate query to make sure of it. It is needed to make sure we collect the latest reports and that equation still holds true. However we missed one tiny little difference here on "<" v.s. "<=" when comparing pending_size (A) to threshold_size (B).. QEMU src only re-query if A<B, but will kickoff switchover if A<=B. I think it means it is possible to happen if A (as an estimate only so far) accidentally equals to B, then re-query won't happen and switchover will proceed without considering new dirtied data. It turns out it was an accident in my commit 7aaa1fc072 when refactoring the code around. Fix this by using the same equation in both places. Fixes: 7aaa1fc072 ("migration: Rewrite the migration complete detect logic") Cc: qemu-stable@nongnu.org Reviewed-by: Juraj Marcin <jmarcin@redhat.com> Link: https://lore.kernel.org/r/20260421202110.306051-3-peterx@redhat.com Signed-off-by: Peter Xu <peterx@redhat.com> --- migration/migration.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/migration/migration.c b/migration/migration.c index XXXXXXX..XXXXXXX 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -XXX,XX +XXX,XX @@ static MigIterateState migration_iteration_run(MigrationState *s) * postcopy started, so ESTIMATE should always match with EXACT * during postcopy phase. */ - if (pending_size < s->threshold_size) { + if (pending_size <= s->threshold_size) { qemu_savevm_state_pending_exact(&must_precopy, &can_postcopy); pending_size = must_precopy + can_postcopy; trace_migrate_pending_exact(pending_size, must_precopy, -- 2.53.0
This stats is only about RAM, make it accurate. This paves way for statistics for all devices. Thanks to Markus, who pointed out that docs/devel/qapi-code-gen.rst has a section "Compatibility considerations" stated: Since type names are not visible in the Client JSON Protocol, types may be freely renamed. Even certain refactorings are invisible, such as splitting members from one type into a common base type. Hence this change is not ABI violation according to the document. While at it, touch up the lines to make it read better, correct the restriction on migration status being 'active' or 'completed': over time we grew too many new status that will also report "ram" section. Cc: Daniel P. Berrangé <berrange@redhat.com> Cc: devel@lists.libvirt.org Reviewed-by: Markus Armbruster <armbru@redhat.com> Reviewed-by: Juraj Marcin <jmarcin@redhat.com> Reviewed-by: Michal Privoznik <mprivozn@redhat.com> Link: https://lore.kernel.org/r/20260421202110.306051-4-peterx@redhat.com Signed-off-by: Peter Xu <peterx@redhat.com> --- docs/about/removed-features.rst | 2 +- qapi/migration.json | 10 +++++----- migration/migration-stats.h | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/about/removed-features.rst b/docs/about/removed-features.rst index XXXXXXX..XXXXXXX 100644 --- a/docs/about/removed-features.rst +++ b/docs/about/removed-features.rst @@ -XXX,XX +XXX,XX @@ was superseded by ``sections``. ``query-migrate`` return value member ``skipped`` (removed in 9.1) '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' -Member ``skipped`` of the ``MigrationStats`` struct hasn't been used +Member ``skipped`` of the ``MigrationRAMStats`` struct hasn't been used for more than 10 years. Removed with no replacement. ``migrate`` command option ``inc`` (removed in 9.1) diff --git a/qapi/migration.json b/qapi/migration.json index XXXXXXX..XXXXXXX 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -XXX,XX +XXX,XX @@ { 'include': 'sockets.json' } ## -# @MigrationStats: +# @MigrationRAMStats: # # Detailed migration status. # @@ -XXX,XX +XXX,XX @@ # # Since: 0.14 ## -{ 'struct': 'MigrationStats', +{ 'struct': 'MigrationRAMStats', 'data': {'transferred': 'int', 'remaining': 'int', 'total': 'int' , 'duplicate': 'int', 'normal': 'int', @@ -XXX,XX +XXX,XX @@ # If this field is not returned, no migration process has been # initiated # -# @ram: `MigrationStats` containing detailed migration status, only -# returned if status is 'active' or 'completed'(since 1.2) +# @ram: Detailed migration RAM statistics, only returned if migration +# is in progress or completed (since 1.2) # # @xbzrle-cache: `XBZRLECacheStats` containing detailed XBZRLE # migration statistics, only returned if XBZRLE feature is on and @@ -XXX,XX +XXX,XX @@ # Since: 0.14 ## { 'struct': 'MigrationInfo', - 'data': {'*status': 'MigrationStatus', '*ram': 'MigrationStats', + 'data': {'*status': 'MigrationStatus', '*ram': 'MigrationRAMStats', '*vfio': 'VfioStats', '*xbzrle-cache': 'XBZRLECacheStats', '*total-time': 'int', diff --git a/migration/migration-stats.h b/migration/migration-stats.h index XXXXXXX..XXXXXXX 100644 --- a/migration/migration-stats.h +++ b/migration/migration-stats.h @@ -XXX,XX +XXX,XX @@ /* * These are the ram migration statistic counters. It is loosely - * based on MigrationStats. + * based on MigrationRAMStats. */ typedef struct { /* -- 2.53.0
Add a field to cache stop size. Note that there's an initial value change in vfio_save_setup for the stop size default, but it shouldn't matter if it is followed with a math of MIN() against VFIO_MIG_DEFAULT_DATA_BUFFER_SIZE. Document that all the three sizes we read from VFIO's uAPI on dirty or stop sizes are estimates, so QEMU needs to always remember they can be anything. Reviewed-by: Avihai Horon <avihaih@nvidia.com> Link: https://lore.kernel.org/r/20260421202110.306051-5-peterx@redhat.com Signed-off-by: Peter Xu <peterx@redhat.com> --- hw/vfio/vfio-migration-internal.h | 8 +++++ hw/vfio/migration.c | 50 ++++++++++++++++++------------- 2 files changed, 38 insertions(+), 20 deletions(-) diff --git a/hw/vfio/vfio-migration-internal.h b/hw/vfio/vfio-migration-internal.h index XXXXXXX..XXXXXXX 100644 --- a/hw/vfio/vfio-migration-internal.h +++ b/hw/vfio/vfio-migration-internal.h @@ -XXX,XX +XXX,XX @@ typedef struct VFIOMigration { void *data_buffer; size_t data_buffer_size; uint64_t mig_flags; + /* + * NOTE: all three sizes cached are reported from VFIO's uAPI, which + * are defined as estimate only. QEMU should not trust these values + * but only use them to do best-effort estimates. Always be prepared + * that these sizes may either grow or even shrink in reality while + * read()ing from the VFIO fds. + */ uint64_t precopy_init_size; uint64_t precopy_dirty_size; + uint64_t stopcopy_size; bool multifd_transfer; VFIOMultifd *multifd; bool initial_data_sent; diff --git a/hw/vfio/migration.c b/hw/vfio/migration.c index XXXXXXX..XXXXXXX 100644 --- a/hw/vfio/migration.c +++ b/hw/vfio/migration.c @@ -XXX,XX +XXX,XX @@ */ #define VFIO_MIG_DEFAULT_DATA_BUFFER_SIZE (1 * MiB) +/* + * Migration size of VFIO devices can be as little as a few KBs or as big as + * many GBs. This value should be big enough to cover the worst case. + */ +#define VFIO_MIG_STOP_COPY_SIZE (100 * GiB) + static unsigned long bytes_transferred; static const char *mig_state_to_str(enum vfio_device_mig_state state) @@ -XXX,XX +XXX,XX @@ static void vfio_migration_cleanup(VFIODevice *vbasedev) migration->data_fd = -1; } -static int vfio_query_stop_copy_size(VFIODevice *vbasedev, - uint64_t *stop_copy_size) +static int vfio_query_stop_copy_size(VFIODevice *vbasedev) { uint64_t buf[DIV_ROUND_UP(sizeof(struct vfio_device_feature) + sizeof(struct vfio_device_feature_mig_data_size), @@ -XXX,XX +XXX,XX @@ static int vfio_query_stop_copy_size(VFIODevice *vbasedev, struct vfio_device_feature *feature = (struct vfio_device_feature *)buf; struct vfio_device_feature_mig_data_size *mig_data_size = (struct vfio_device_feature_mig_data_size *)feature->data; + VFIOMigration *migration = vbasedev->migration; feature->argsz = sizeof(buf); feature->flags = VFIO_DEVICE_FEATURE_GET | VFIO_DEVICE_FEATURE_MIG_DATA_SIZE; if (ioctl(vbasedev->fd, VFIO_DEVICE_FEATURE, feature)) { + /* + * If getting pending migration size fails, VFIO_MIG_STOP_COPY_SIZE + * is reported so downtime limit won't be violated. + */ + migration->stopcopy_size = VFIO_MIG_STOP_COPY_SIZE; return -errno; } - *stop_copy_size = mig_data_size->stop_copy_length; + migration->stopcopy_size = mig_data_size->stop_copy_length; return 0; } @@ -XXX,XX +XXX,XX @@ static void vfio_update_estimated_pending_data(VFIOMigration *migration, return; } + /* + * The total size remaining requires separate accounting. Do not trust + * the counter, so what we have read() may be more than what reported. + */ + if (migration->stopcopy_size > data_size) { + migration->stopcopy_size -= data_size; + } else { + migration->stopcopy_size = 0; + } + if (migration->precopy_init_size) { uint64_t init_size = MIN(migration->precopy_init_size, data_size); @@ -XXX,XX +XXX,XX @@ static int vfio_save_setup(QEMUFile *f, void *opaque, Error **errp) { VFIODevice *vbasedev = opaque; VFIOMigration *migration = vbasedev->migration; - uint64_t stop_copy_size = VFIO_MIG_DEFAULT_DATA_BUFFER_SIZE; int ret; if (!vfio_multifd_setup(vbasedev, false, errp)) { @@ -XXX,XX +XXX,XX @@ static int vfio_save_setup(QEMUFile *f, void *opaque, Error **errp) qemu_put_be64(f, VFIO_MIG_FLAG_DEV_SETUP_STATE); - vfio_query_stop_copy_size(vbasedev, &stop_copy_size); + vfio_query_stop_copy_size(vbasedev); migration->data_buffer_size = MIN(VFIO_MIG_DEFAULT_DATA_BUFFER_SIZE, - stop_copy_size); + migration->stopcopy_size); migration->data_buffer = g_try_malloc0(migration->data_buffer_size); if (!migration->data_buffer) { error_setg(errp, "%s: Failed to allocate migration data buffer", @@ -XXX,XX +XXX,XX @@ static void vfio_state_pending_estimate(void *opaque, uint64_t *must_precopy, migration->precopy_dirty_size); } -/* - * Migration size of VFIO devices can be as little as a few KBs or as big as - * many GBs. This value should be big enough to cover the worst case. - */ -#define VFIO_MIG_STOP_COPY_SIZE (100 * GiB) - static void vfio_state_pending_exact(void *opaque, uint64_t *must_precopy, uint64_t *can_postcopy) { VFIODevice *vbasedev = opaque; VFIOMigration *migration = vbasedev->migration; - uint64_t stop_copy_size = VFIO_MIG_STOP_COPY_SIZE; - /* - * If getting pending migration size fails, VFIO_MIG_STOP_COPY_SIZE is - * reported so downtime limit won't be violated. - */ - vfio_query_stop_copy_size(vbasedev, &stop_copy_size); - *must_precopy += stop_copy_size; + vfio_query_stop_copy_size(vbasedev); + *must_precopy += migration->stopcopy_size; if (vfio_device_state_is_precopy(vbasedev)) { vfio_query_precopy_size(migration); } trace_vfio_state_pending_exact(vbasedev->name, *must_precopy, *can_postcopy, - stop_copy_size, migration->precopy_init_size, + migration->stopcopy_size, + migration->precopy_init_size, migration->precopy_dirty_size); } -- 2.53.0
These two APIs are a slight duplication. For example, there're a few users that directly pass in the same function. It might also be error prone to provide two hooks, so that it's easier to happen one module report different things via the two hooks. In reality, they should always report the same thing, only about whether we should use a fast-path when the slow path might be too slow, as QEMU may query these information quite frequently during migration process. Merge it into one API, provide a bool showing if the query is an exact query or not. No functional change intended. Export qemu_savevm_query_pending(). We should use the new API here provided when there're new users to do the query. This will happen very soon. Cc: Halil Pasic <pasic@linux.ibm.com> Cc: Christian Borntraeger <borntraeger@linux.ibm.com> Cc: Eric Farman <farman@linux.ibm.com> Cc: Matthew Rosato <mjrosato@linux.ibm.com> Cc: Richard Henderson <richard.henderson@linaro.org> Cc: Ilya Leoshkevich <iii@linux.ibm.com> Cc: David Hildenbrand <david@kernel.org> Cc: Cornelia Huck <cohuck@redhat.com> Cc: Eric Blake <eblake@redhat.com> Cc: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru> Cc: John Snow <jsnow@redhat.com> Reviewed-by: Jason J. Herne <jjherne@linux.ibm.com> Reviewed-by: Juraj Marcin <jmarcin@redhat.com> Reviewed-by: Avihai Horon <avihaih@nvidia.com> Acked-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru> Link: https://lore.kernel.org/r/20260421202110.306051-6-peterx@redhat.com Signed-off-by: Peter Xu <peterx@redhat.com> --- docs/devel/migration/main.rst | 9 ++---- docs/devel/migration/vfio.rst | 9 ++---- include/migration/register.h | 52 ++++++++++++---------------------- migration/savevm.h | 3 ++ hw/s390x/s390-stattrib.c | 9 +++--- hw/vfio/migration.c | 48 ++++++++++++++----------------- migration/block-dirty-bitmap.c | 10 +++---- migration/ram.c | 33 +++++++-------------- migration/savevm.c | 42 +++++++++++++-------------- hw/vfio/trace-events | 3 +- 10 files changed, 86 insertions(+), 132 deletions(-) diff --git a/docs/devel/migration/main.rst b/docs/devel/migration/main.rst index XXXXXXX..XXXXXXX 100644 --- a/docs/devel/migration/main.rst +++ b/docs/devel/migration/main.rst @@ -XXX,XX +XXX,XX @@ An iterative device must provide: - A ``load_setup`` function that initialises the data structures on the destination. - - A ``state_pending_exact`` function that indicates how much more - data we must save. The core migration code will use this to - determine when to pause the CPUs and complete the migration. - - - A ``state_pending_estimate`` function that indicates how much more - data we must save. When the estimated amount is smaller than the - threshold, we call ``state_pending_exact``. + - A ``save_query_pending`` function that indicates how much more + data we must save. - A ``save_live_iterate`` function should send a chunk of data until the point that stream bandwidth limits tell it to stop. Each call diff --git a/docs/devel/migration/vfio.rst b/docs/devel/migration/vfio.rst index XXXXXXX..XXXXXXX 100644 --- a/docs/devel/migration/vfio.rst +++ b/docs/devel/migration/vfio.rst @@ -XXX,XX +XXX,XX @@ VFIO implements the device hooks for the iterative approach as follows: * A ``load_setup`` function that sets the VFIO device on the destination in _RESUMING state. -* A ``state_pending_estimate`` function that reports an estimate of the - remaining pre-copy data that the vendor driver has yet to save for the VFIO - device. - -* A ``state_pending_exact`` function that reads pending_bytes from the vendor - driver, which indicates the amount of data that the vendor driver has yet to - save for the VFIO device. +* A ``save_query_pending`` function that reports the remaining data that + the vendor driver has yet to save for the VFIO device. * An ``is_active_iterate`` function that indicates ``save_live_iterate`` is active only when the VFIO device is in pre-copy states. diff --git a/include/migration/register.h b/include/migration/register.h index XXXXXXX..XXXXXXX 100644 --- a/include/migration/register.h +++ b/include/migration/register.h @@ -XXX,XX +XXX,XX @@ #include "hw/core/vmstate-if.h" +typedef struct MigPendingData { + /* Amount of pending bytes can be transferred in precopy or stopcopy */ + uint64_t precopy_bytes; + /* Amount of pending bytes can be transferred in postcopy */ + uint64_t postcopy_bytes; +} MigPendingData; + /** * struct SaveVMHandlers: handler structure to finely control * migration of complex subsystems and devices, such as RAM, block and @@ -XXX,XX +XXX,XX @@ typedef struct SaveVMHandlers { bool (*save_postcopy_prepare)(QEMUFile *f, void *opaque, Error **errp); /** - * @state_pending_estimate - * - * This estimates the remaining data to transfer + * @save_query_pending * - * Sum of @can_postcopy and @must_postcopy is the whole amount of - * pending data. - * - * @opaque: data pointer passed to register_savevm_live() - * @must_precopy: amount of data that must be migrated in precopy - * or in stopped state, i.e. that must be migrated - * before target start. - * @can_postcopy: amount of data that can be migrated in postcopy - * or in stopped state, i.e. after target start. - * Some can also be migrated during precopy (RAM). - * Some must be migrated after source stops - * (block-dirty-bitmap) - */ - void (*state_pending_estimate)(void *opaque, uint64_t *must_precopy, - uint64_t *can_postcopy); - - /** - * @state_pending_exact + * This estimates the remaining data to transfer on the source side. * - * This calculates the exact remaining data to transfer + * When @exact is true, a module must report accurate results. When + * @exact is false, a module may report estimates. * - * Sum of @can_postcopy and @must_postcopy is the whole amount of - * pending data. + * It's highly recommended that modules implement a faster version of + * the query path (for example, by proper caching on the counters) if + * an accurate query will be time-consuming. * * @opaque: data pointer passed to register_savevm_live() - * @must_precopy: amount of data that must be migrated in precopy - * or in stopped state, i.e. that must be migrated - * before target start. - * @can_postcopy: amount of data that can be migrated in postcopy - * or in stopped state, i.e. after target start. - * Some can also be migrated during precopy (RAM). - * Some must be migrated after source stops - * (block-dirty-bitmap) + * @pending: pointer to a MigPendingData struct + * @exact: set to true for an accurate (slow) query */ - void (*state_pending_exact)(void *opaque, uint64_t *must_precopy, - uint64_t *can_postcopy); + void (*save_query_pending)(void *opaque, MigPendingData *pending, + bool exact); /** * @load_state diff --git a/migration/savevm.h b/migration/savevm.h index XXXXXXX..XXXXXXX 100644 --- a/migration/savevm.h +++ b/migration/savevm.h @@ -XXX,XX +XXX,XX @@ #ifndef MIGRATION_SAVEVM_H #define MIGRATION_SAVEVM_H +#include "migration/register.h" + #define QEMU_VM_FILE_MAGIC 0x5145564d #define QEMU_VM_FILE_VERSION_COMPAT 0x00000002 #define QEMU_VM_FILE_VERSION 0x00000003 @@ -XXX,XX +XXX,XX @@ int qemu_savevm_state_iterate(QEMUFile *f, bool postcopy); void qemu_savevm_state_cleanup(void); void qemu_savevm_state_complete_postcopy(QEMUFile *f); int qemu_savevm_state_complete_precopy(MigrationState *s); +void qemu_savevm_query_pending(MigPendingData *pending, bool exact); void qemu_savevm_state_pending_exact(uint64_t *must_precopy, uint64_t *can_postcopy); void qemu_savevm_state_pending_estimate(uint64_t *must_precopy, diff --git a/hw/s390x/s390-stattrib.c b/hw/s390x/s390-stattrib.c index XXXXXXX..XXXXXXX 100644 --- a/hw/s390x/s390-stattrib.c +++ b/hw/s390x/s390-stattrib.c @@ -XXX,XX +XXX,XX @@ static int cmma_save_setup(QEMUFile *f, void *opaque, Error **errp) return 0; } -static void cmma_state_pending(void *opaque, uint64_t *must_precopy, - uint64_t *can_postcopy) +static void cmma_state_pending(void *opaque, MigPendingData *pending, + bool exact) { S390StAttribState *sas = S390_STATTRIB(opaque); S390StAttribClass *sac = S390_STATTRIB_GET_CLASS(sas); long long res = sac->get_dirtycount(sas); if (res >= 0) { - *must_precopy += res; + pending->precopy_bytes += res; } } @@ -XXX,XX +XXX,XX @@ static SaveVMHandlers savevm_s390_stattrib_handlers = { .save_setup = cmma_save_setup, .save_live_iterate = cmma_save_iterate, .save_complete = cmma_save_complete, - .state_pending_exact = cmma_state_pending, - .state_pending_estimate = cmma_state_pending, + .save_query_pending = cmma_state_pending, .save_cleanup = cmma_save_cleanup, .load_state = cmma_load, .is_active = cmma_active, diff --git a/hw/vfio/migration.c b/hw/vfio/migration.c index XXXXXXX..XXXXXXX 100644 --- a/hw/vfio/migration.c +++ b/hw/vfio/migration.c @@ -XXX,XX +XXX,XX @@ static void vfio_save_cleanup(void *opaque) trace_vfio_save_cleanup(vbasedev->name); } -static void vfio_state_pending_estimate(void *opaque, uint64_t *must_precopy, - uint64_t *can_postcopy) +static void vfio_state_pending_sync(VFIODevice *vbasedev) { - VFIODevice *vbasedev = opaque; VFIOMigration *migration = vbasedev->migration; - if (!vfio_device_state_is_precopy(vbasedev)) { - return; - } - - *must_precopy += - migration->precopy_init_size + migration->precopy_dirty_size; + vfio_query_stop_copy_size(vbasedev); - trace_vfio_state_pending_estimate(vbasedev->name, *must_precopy, - *can_postcopy, - migration->precopy_init_size, - migration->precopy_dirty_size); + if (vfio_device_state_is_precopy(vbasedev)) { + vfio_query_precopy_size(migration); + } } -static void vfio_state_pending_exact(void *opaque, uint64_t *must_precopy, - uint64_t *can_postcopy) +static void vfio_state_pending(void *opaque, MigPendingData *pending, + bool exact) { VFIODevice *vbasedev = opaque; VFIOMigration *migration = vbasedev->migration; + uint64_t remain; - vfio_query_stop_copy_size(vbasedev); - *must_precopy += migration->stopcopy_size; - - if (vfio_device_state_is_precopy(vbasedev)) { - vfio_query_precopy_size(migration); + if (exact) { + vfio_state_pending_sync(vbasedev); + remain = migration->stopcopy_size; + } else { + if (!vfio_device_state_is_precopy(vbasedev)) { + return; + } + remain = migration->precopy_init_size + migration->precopy_dirty_size; } - trace_vfio_state_pending_exact(vbasedev->name, *must_precopy, *can_postcopy, - migration->stopcopy_size, - migration->precopy_init_size, - migration->precopy_dirty_size); + pending->precopy_bytes += remain; + + trace_vfio_state_pending(vbasedev->name, migration->stopcopy_size, + migration->precopy_init_size, + migration->precopy_dirty_size, exact); } static bool vfio_is_active_iterate(void *opaque) @@ -XXX,XX +XXX,XX @@ static const SaveVMHandlers savevm_vfio_handlers = { .save_prepare = vfio_save_prepare, .save_setup = vfio_save_setup, .save_cleanup = vfio_save_cleanup, - .state_pending_estimate = vfio_state_pending_estimate, - .state_pending_exact = vfio_state_pending_exact, + .save_query_pending = vfio_state_pending, .is_active_iterate = vfio_is_active_iterate, .save_live_iterate = vfio_save_iterate, .save_complete = vfio_save_complete_precopy, diff --git a/migration/block-dirty-bitmap.c b/migration/block-dirty-bitmap.c index XXXXXXX..XXXXXXX 100644 --- a/migration/block-dirty-bitmap.c +++ b/migration/block-dirty-bitmap.c @@ -XXX,XX +XXX,XX @@ static int dirty_bitmap_save_complete(QEMUFile *f, void *opaque) return 0; } -static void dirty_bitmap_state_pending(void *opaque, - uint64_t *must_precopy, - uint64_t *can_postcopy) +static void dirty_bitmap_state_pending(void *opaque, MigPendingData *data, + bool exact) { DBMSaveState *s = &((DBMState *)opaque)->save; SaveBitmapState *dbms; @@ -XXX,XX +XXX,XX @@ static void dirty_bitmap_state_pending(void *opaque, trace_dirty_bitmap_state_pending(pending); - *can_postcopy += pending; + data->postcopy_bytes += pending; } /* First occurrence of this bitmap. It should be created if doesn't exist */ @@ -XXX,XX +XXX,XX @@ static SaveVMHandlers savevm_dirty_bitmap_handlers = { .save_setup = dirty_bitmap_save_setup, .save_complete = dirty_bitmap_save_complete, .has_postcopy = dirty_bitmap_has_postcopy, - .state_pending_exact = dirty_bitmap_state_pending, - .state_pending_estimate = dirty_bitmap_state_pending, + .save_query_pending = dirty_bitmap_state_pending, .save_live_iterate = dirty_bitmap_save_iterate, .is_active_iterate = dirty_bitmap_is_active_iterate, .load_state = dirty_bitmap_load, diff --git a/migration/ram.c b/migration/ram.c index XXXXXXX..XXXXXXX 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -XXX,XX +XXX,XX @@ static int ram_save_complete(QEMUFile *f, void *opaque) return qemu_fflush(f); } -static void ram_state_pending_estimate(void *opaque, uint64_t *must_precopy, - uint64_t *can_postcopy) -{ - RAMState **temp = opaque; - RAMState *rs = *temp; - - uint64_t remaining_size = rs->migration_dirty_pages * TARGET_PAGE_SIZE; - - if (migrate_postcopy_ram()) { - /* We can do postcopy, and all the data is postcopiable */ - *can_postcopy += remaining_size; - } else { - *must_precopy += remaining_size; - } -} - -static void ram_state_pending_exact(void *opaque, uint64_t *must_precopy, - uint64_t *can_postcopy) +static void ram_state_pending(void *opaque, MigPendingData *pending, + bool exact) { RAMState **temp = opaque; RAMState *rs = *temp; uint64_t remaining_size; - if (!migration_in_postcopy()) { + /* + * Sync is not needed either with: (1) a fast query, or (2) after + * postcopy has started (no new dirty will generate anymore). + */ + if (exact && !migration_in_postcopy()) { bql_lock(); WITH_RCU_READ_LOCK_GUARD() { migration_bitmap_sync_precopy(false); @@ -XXX,XX +XXX,XX @@ static void ram_state_pending_exact(void *opaque, uint64_t *must_precopy, if (migrate_postcopy_ram()) { /* We can do postcopy, and all the data is postcopiable */ - *can_postcopy += remaining_size; + pending->postcopy_bytes += remaining_size; } else { - *must_precopy += remaining_size; + pending->precopy_bytes += remaining_size; } } @@ -XXX,XX +XXX,XX @@ static SaveVMHandlers savevm_ram_handlers = { .save_live_iterate = ram_save_iterate, .save_complete = ram_save_complete, .has_postcopy = ram_has_postcopy, - .state_pending_exact = ram_state_pending_exact, - .state_pending_estimate = ram_state_pending_estimate, + .save_query_pending = ram_state_pending, .load_state = ram_load, .save_cleanup = ram_save_cleanup, .load_setup = ram_load_setup, diff --git a/migration/savevm.c b/migration/savevm.c index XXXXXXX..XXXXXXX 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -XXX,XX +XXX,XX @@ int qemu_savevm_state_complete_precopy(MigrationState *s) return qemu_fflush(f); } -/* Give an estimate of the amount left to be transferred, - * the result is split into the amount for units that can and - * for units that can't do postcopy. - */ -void qemu_savevm_state_pending_estimate(uint64_t *must_precopy, - uint64_t *can_postcopy) +void qemu_savevm_query_pending(MigPendingData *pending, bool exact) { SaveStateEntry *se; - *must_precopy = 0; - *can_postcopy = 0; + pending->precopy_bytes = 0; + pending->postcopy_bytes = 0; QTAILQ_FOREACH(se, &savevm_state.handlers, entry) { - if (!se->ops || !se->ops->state_pending_estimate) { + if (!se->ops || !se->ops->save_query_pending) { continue; } if (!qemu_savevm_state_active(se)) { continue; } - se->ops->state_pending_estimate(se->opaque, must_precopy, can_postcopy); + se->ops->save_query_pending(se->opaque, pending, exact); } } +void qemu_savevm_state_pending_estimate(uint64_t *must_precopy, + uint64_t *can_postcopy) +{ + MigPendingData pending; + + qemu_savevm_query_pending(&pending, false); + + *must_precopy = pending.precopy_bytes; + *can_postcopy = pending.postcopy_bytes; +} + void qemu_savevm_state_pending_exact(uint64_t *must_precopy, uint64_t *can_postcopy) { - SaveStateEntry *se; + MigPendingData pending; - *must_precopy = 0; - *can_postcopy = 0; + qemu_savevm_query_pending(&pending, true); - QTAILQ_FOREACH(se, &savevm_state.handlers, entry) { - if (!se->ops || !se->ops->state_pending_exact) { - continue; - } - if (!qemu_savevm_state_active(se)) { - continue; - } - se->ops->state_pending_exact(se->opaque, must_precopy, can_postcopy); - } + *must_precopy = pending.precopy_bytes; + *can_postcopy = pending.postcopy_bytes; } void qemu_savevm_state_cleanup(void) diff --git a/hw/vfio/trace-events b/hw/vfio/trace-events index XXXXXXX..XXXXXXX 100644 --- a/hw/vfio/trace-events +++ b/hw/vfio/trace-events @@ -XXX,XX +XXX,XX @@ vfio_save_device_config_state(const char *name) " (%s)" vfio_save_iterate(const char *name, uint64_t precopy_init_size, uint64_t precopy_dirty_size) " (%s) precopy initial size %"PRIu64" precopy dirty size %"PRIu64 vfio_save_iterate_start(const char *name) " (%s)" vfio_save_setup(const char *name, uint64_t data_buffer_size) " (%s) data buffer size %"PRIu64 -vfio_state_pending_estimate(const char *name, uint64_t precopy, uint64_t postcopy, uint64_t precopy_init_size, uint64_t precopy_dirty_size) " (%s) precopy %"PRIu64" postcopy %"PRIu64" precopy initial size %"PRIu64" precopy dirty size %"PRIu64 -vfio_state_pending_exact(const char *name, uint64_t precopy, uint64_t postcopy, uint64_t stopcopy_size, uint64_t precopy_init_size, uint64_t precopy_dirty_size) " (%s) precopy %"PRIu64" postcopy %"PRIu64" stopcopy size %"PRIu64" precopy initial size %"PRIu64" precopy dirty size %"PRIu64 +vfio_state_pending(const char *name, uint64_t stopcopy_size, uint64_t precopy_init_size, uint64_t precopy_dirty_size, bool exact) " (%s) stopcopy size %"PRIu64" precopy initial size %"PRIu64" precopy dirty size %"PRIu64 " exact %d" vfio_vmstate_change(const char *name, int running, const char *reason, const char *dev_state) " (%s) running %d reason %s device state %s" vfio_vmstate_change_prepare(const char *name, int running, const char *reason, const char *dev_state) " (%s) running %d reason %s device state %s" -- 2.53.0
It's easier to use the new API directly in the migration iterations. This also paves way for follow up patches to add new data to report directly to the iterator function. When at it, merge the tracepoints too into one. No functional change intended. Reviewed-by: Juraj Marcin <jmarcin@redhat.com> Reviewed-by: Avihai Horon <avihaih@nvidia.com> Link: https://lore.kernel.org/r/20260421202110.306051-7-peterx@redhat.com Signed-off-by: Peter Xu <peterx@redhat.com> --- migration/savevm.h | 4 ---- migration/migration.c | 16 +++++++--------- migration/savevm.c | 23 ++--------------------- migration/trace-events | 3 +-- 4 files changed, 10 insertions(+), 36 deletions(-) diff --git a/migration/savevm.h b/migration/savevm.h index XXXXXXX..XXXXXXX 100644 --- a/migration/savevm.h +++ b/migration/savevm.h @@ -XXX,XX +XXX,XX @@ void qemu_savevm_state_cleanup(void); void qemu_savevm_state_complete_postcopy(QEMUFile *f); int qemu_savevm_state_complete_precopy(MigrationState *s); void qemu_savevm_query_pending(MigPendingData *pending, bool exact); -void qemu_savevm_state_pending_exact(uint64_t *must_precopy, - uint64_t *can_postcopy); -void qemu_savevm_state_pending_estimate(uint64_t *must_precopy, - uint64_t *can_postcopy); int qemu_savevm_state_complete_precopy_iterable(QEMUFile *f, bool in_postcopy); bool qemu_savevm_state_postcopy_prepare(QEMUFile *f, Error **errp); void qemu_savevm_state_end(QEMUFile *f); diff --git a/migration/migration.c b/migration/migration.c index XXXXXXX..XXXXXXX 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -XXX,XX +XXX,XX @@ typedef enum { */ static MigIterateState migration_iteration_run(MigrationState *s) { - uint64_t must_precopy, can_postcopy, pending_size; Error *local_err = NULL; bool in_postcopy = (s->state == MIGRATION_STATUS_POSTCOPY_DEVICE || s->state == MIGRATION_STATUS_POSTCOPY_ACTIVE); bool can_switchover = migration_can_switchover(s); + MigPendingData pending = { }; + uint64_t pending_size; bool complete_ready; /* Fast path - get the estimated amount of pending data */ - qemu_savevm_state_pending_estimate(&must_precopy, &can_postcopy); - pending_size = must_precopy + can_postcopy; - trace_migrate_pending_estimate(pending_size, must_precopy, can_postcopy); + qemu_savevm_query_pending(&pending, false); + pending_size = pending.precopy_bytes + pending.postcopy_bytes; if (in_postcopy) { /* @@ -XXX,XX +XXX,XX @@ static MigIterateState migration_iteration_run(MigrationState *s) * during postcopy phase. */ if (pending_size <= s->threshold_size) { - qemu_savevm_state_pending_exact(&must_precopy, &can_postcopy); - pending_size = must_precopy + can_postcopy; - trace_migrate_pending_exact(pending_size, must_precopy, - can_postcopy); + qemu_savevm_query_pending(&pending, true); + pending_size = pending.precopy_bytes + pending.postcopy_bytes; } /* Should we switch to postcopy now? */ - if (must_precopy <= s->threshold_size && + if (pending.precopy_bytes <= s->threshold_size && can_switchover && qatomic_read(&s->start_postcopy)) { if (postcopy_start(s, &local_err)) { migrate_error_propagate(s, error_copy(local_err)); diff --git a/migration/savevm.c b/migration/savevm.c index XXXXXXX..XXXXXXX 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -XXX,XX +XXX,XX @@ void qemu_savevm_query_pending(MigPendingData *pending, bool exact) } se->ops->save_query_pending(se->opaque, pending, exact); } -} - -void qemu_savevm_state_pending_estimate(uint64_t *must_precopy, - uint64_t *can_postcopy) -{ - MigPendingData pending; - - qemu_savevm_query_pending(&pending, false); - - *must_precopy = pending.precopy_bytes; - *can_postcopy = pending.postcopy_bytes; -} - -void qemu_savevm_state_pending_exact(uint64_t *must_precopy, - uint64_t *can_postcopy) -{ - MigPendingData pending; - - qemu_savevm_query_pending(&pending, true); - *must_precopy = pending.precopy_bytes; - *can_postcopy = pending.postcopy_bytes; + trace_qemu_savevm_query_pending(exact, pending->precopy_bytes, + pending->postcopy_bytes); } void qemu_savevm_state_cleanup(void) diff --git a/migration/trace-events b/migration/trace-events index XXXXXXX..XXXXXXX 100644 --- a/migration/trace-events +++ b/migration/trace-events @@ -XXX,XX +XXX,XX @@ qemu_loadvm_state_section_partend(uint32_t section_id) "%u" qemu_loadvm_state_post_main(int ret) "%d" qemu_loadvm_state_section_startfull(uint32_t section_id, const char *idstr, uint32_t instance_id, uint32_t version_id) "%u(%s) %u %u" qemu_savevm_send_packaged(void) "" +qemu_savevm_query_pending(bool exact, uint64_t precopy, uint64_t postcopy) "exact=%d, precopy=%"PRIu64", postcopy=%"PRIu64 loadvm_state_switchover_ack_needed(unsigned int switchover_ack_pending_num) "Switchover ack pending num=%u" loadvm_state_setup(void) "" loadvm_state_cleanup(void) "" @@ -XXX,XX +XXX,XX @@ migration_cleanup(void) "" migrate_error(const char *error_desc) "error=%s" migration_cancel(void) "" migrate_handle_rp_req_pages(const char *rbname, size_t start, size_t len) "in %s at 0x%zx len 0x%zx" -migrate_pending_exact(uint64_t size, uint64_t pre, uint64_t post) "exact pending size %" PRIu64 " (pre = %" PRIu64 " post=%" PRIu64 ")" -migrate_pending_estimate(uint64_t size, uint64_t pre, uint64_t post) "estimate pending size %" PRIu64 " (pre = %" PRIu64 " post=%" PRIu64 ")" migrate_send_rp_message(int msg_type, uint16_t len) "%d: len %d" migrate_send_rp_recv_bitmap(char *name, int64_t size) "block '%s' size 0x%"PRIi64 migration_completion_file_err(void) "" -- 2.53.0
Allow modules to report data that can only be migrated after VM is stopped. When this concept is introduced, we will need to account stopcopy size to be part of pending_size as before. However, when there're data only can be migrated in stopcopy phase, it means the old "pending_size" may not always be able to reach low enough to kickoff an slow version of query sync. It used to be almost guaranteed to happen as all prior iterative modules doesn't have stopcopy only data. VFIO may change that fact by having some data that must be copied during stop phase. So we need to make sure QEMU will kickoff a synchronized version of query pending when all precopy data is migrated. This might be important to VFIO to keep making progress even if the downtime cannot yet be satisfied. So far, this patch should introduce no functional change, as no module yet report stopcopy size. This paves way for VFIO to properly report its pending data sizes, which will start to include stop-only data. Reviewed-by: Avihai Horon <avihaih@nvidia.com> Reviewed-by: Juraj Marcin <jmarcin@redhat.com> Link: https://lore.kernel.org/r/20260421202110.306051-8-peterx@redhat.com Signed-off-by: Peter Xu <peterx@redhat.com> --- include/migration/register.h | 7 ++++ migration/migration.c | 65 ++++++++++++++++++++++++++++++------ migration/savevm.c | 10 ++++-- migration/trace-events | 2 +- 4 files changed, 70 insertions(+), 14 deletions(-) diff --git a/include/migration/register.h b/include/migration/register.h index XXXXXXX..XXXXXXX 100644 --- a/include/migration/register.h +++ b/include/migration/register.h @@ -XXX,XX +XXX,XX @@ typedef struct MigPendingData { uint64_t precopy_bytes; /* Amount of pending bytes can be transferred in postcopy */ uint64_t postcopy_bytes; + /* Amount of pending bytes can be transferred only in stopcopy */ + uint64_t stopcopy_bytes; + /* + * Total pending data, modules do not need to update this field, it + * will be automatically calculated by migration core API. + */ + uint64_t total_bytes; } MigPendingData; /** diff --git a/migration/migration.c b/migration/migration.c index XXXXXXX..XXXXXXX 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -XXX,XX +XXX,XX @@ typedef enum { MIG_ITERATE_BREAK, /* Break the loop */ } MigIterateState; +/* Are we ready to move to the next iteration phase? */ +static bool migration_iteration_next_ready(MigrationState *s, + MigPendingData *pending) +{ + /* + * If the estimated values already suggest us to switchover, mark this + * iteration finished, time to do a slow sync. + */ + if (pending->total_bytes <= s->threshold_size) { + return true; + } + + /* + * Since we may have modules reporting stop-only data, we also want to + * re-query with slow mode if all precopy data is moved over. This + * will also mark the current iteration done. + * + * This could happen when e.g. a module (like, VFIO) reports stopcopy + * size too large so it will never yet satisfy the downtime with the + * current setup (above check). Here, slow version of re-query helps + * because we keep trying the best to move whatever we have. + */ + if (pending->precopy_bytes == 0) { + return true; + } + + return false; +} + +static void migration_iteration_go_next(MigPendingData *pending) +{ + /* + * Do a slow sync will achieve this. TODO: move RAM iteration code + * into the core layer. + */ + qemu_savevm_query_pending(pending, true); +} + +static bool postcopy_should_start(MigrationState *s, MigPendingData *pending) +{ + /* If postcopy's switchver will violate user specified downtime, stop */ + if (pending->precopy_bytes + pending->stopcopy_bytes > s->threshold_size) { + return false; + } + + return qatomic_read(&s->start_postcopy); +} + /* * Return true if continue to the next iteration directly, false * otherwise. @@ -XXX,XX +XXX,XX @@ static MigIterateState migration_iteration_run(MigrationState *s) s->state == MIGRATION_STATUS_POSTCOPY_ACTIVE); bool can_switchover = migration_can_switchover(s); MigPendingData pending = { }; - uint64_t pending_size; bool complete_ready; /* Fast path - get the estimated amount of pending data */ qemu_savevm_query_pending(&pending, false); - pending_size = pending.precopy_bytes + pending.postcopy_bytes; if (in_postcopy) { /* @@ -XXX,XX +XXX,XX @@ static MigIterateState migration_iteration_run(MigrationState *s) * postcopy completion doesn't rely on can_switchover, because when * POSTCOPY_ACTIVE it means switchover already happened. */ - complete_ready = !pending_size; + complete_ready = !pending.total_bytes; if (s->state == MIGRATION_STATUS_POSTCOPY_DEVICE && (s->postcopy_package_loaded || complete_ready)) { /* @@ -XXX,XX +XXX,XX @@ static MigIterateState migration_iteration_run(MigrationState *s) * postcopy started, so ESTIMATE should always match with EXACT * during postcopy phase. */ - if (pending_size <= s->threshold_size) { - qemu_savevm_query_pending(&pending, true); - pending_size = pending.precopy_bytes + pending.postcopy_bytes; + if (migration_iteration_next_ready(s, &pending)) { + migration_iteration_go_next(&pending); } /* Should we switch to postcopy now? */ - if (pending.precopy_bytes <= s->threshold_size && - can_switchover && qatomic_read(&s->start_postcopy)) { + if (can_switchover && postcopy_should_start(s, &pending)) { if (postcopy_start(s, &local_err)) { migrate_error_propagate(s, error_copy(local_err)); error_report_err(local_err); @@ -XXX,XX +XXX,XX @@ static MigIterateState migration_iteration_run(MigrationState *s) * (2) Pending size is no more than the threshold specified * (which was calculated from expected downtime) */ - complete_ready = can_switchover && (pending_size <= s->threshold_size); + complete_ready = can_switchover && + (pending.total_bytes <= s->threshold_size); } if (complete_ready) { - trace_migration_thread_low_pending(pending_size); + trace_migration_thread_low_pending(pending.total_bytes); migration_completion(s); return MIG_ITERATE_BREAK; } diff --git a/migration/savevm.c b/migration/savevm.c index XXXXXXX..XXXXXXX 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -XXX,XX +XXX,XX @@ void qemu_savevm_query_pending(MigPendingData *pending, bool exact) { SaveStateEntry *se; - pending->precopy_bytes = 0; - pending->postcopy_bytes = 0; + memset(pending, 0, sizeof(*pending)); QTAILQ_FOREACH(se, &savevm_state.handlers, entry) { if (!se->ops || !se->ops->save_query_pending) { @@ -XXX,XX +XXX,XX @@ void qemu_savevm_query_pending(MigPendingData *pending, bool exact) se->ops->save_query_pending(se->opaque, pending, exact); } + pending->total_bytes = pending->precopy_bytes + + pending->stopcopy_bytes + pending->postcopy_bytes; + trace_qemu_savevm_query_pending(exact, pending->precopy_bytes, - pending->postcopy_bytes); + pending->stopcopy_bytes, + pending->postcopy_bytes, + pending->total_bytes); } void qemu_savevm_state_cleanup(void) diff --git a/migration/trace-events b/migration/trace-events index XXXXXXX..XXXXXXX 100644 --- a/migration/trace-events +++ b/migration/trace-events @@ -XXX,XX +XXX,XX @@ qemu_loadvm_state_section_partend(uint32_t section_id) "%u" qemu_loadvm_state_post_main(int ret) "%d" qemu_loadvm_state_section_startfull(uint32_t section_id, const char *idstr, uint32_t instance_id, uint32_t version_id) "%u(%s) %u %u" qemu_savevm_send_packaged(void) "" -qemu_savevm_query_pending(bool exact, uint64_t precopy, uint64_t postcopy) "exact=%d, precopy=%"PRIu64", postcopy=%"PRIu64 +qemu_savevm_query_pending(bool exact, uint64_t precopy, uint64_t stopcopy, uint64_t postcopy, uint64_t total) "exact=%d, precopy=%"PRIu64", stopcopy=%"PRIu64", postcopy=%"PRIu64", total=%"PRIu64 loadvm_state_switchover_ack_needed(unsigned int switchover_ack_pending_num) "Switchover ack pending num=%u" loadvm_state_setup(void) "" loadvm_state_cleanup(void) "" -- 2.53.0
VFIO reports different things in its fast/slow version of query pending results. It was because it wants to make sure precopy data can reach 0, which is needed to make sure sync queries will happen periodically over time. Now with stopcopy size reporting facility it doesn't need this hack anymore. Fix this by reporting the same values in fast/slow versions of query pending request, except that the slow version will do a slow sync with the hardwares. When at it, removing the special casing for vfio_device_state_is_precopy() which may reporting nothing in a fast query. Then ther reporting will be consistent to VFIO devices that do not support precopy phase. Copy stable might be too much; just skip it and skip the Fixes. Reviewed-by: Avihai Horon <avihaih@nvidia.com> Tested-by: Avihai Horon <avihaih@nvidia.com> Link: https://lore.kernel.org/r/20260421202110.306051-9-peterx@redhat.com Signed-off-by: Peter Xu <peterx@redhat.com> --- hw/vfio/migration.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/hw/vfio/migration.c b/hw/vfio/migration.c index XXXXXXX..XXXXXXX 100644 --- a/hw/vfio/migration.c +++ b/hw/vfio/migration.c @@ -XXX,XX +XXX,XX @@ static void vfio_state_pending(void *opaque, MigPendingData *pending, { VFIODevice *vbasedev = opaque; VFIOMigration *migration = vbasedev->migration; - uint64_t remain; + uint64_t precopy_size, stopcopy_size; if (exact) { vfio_state_pending_sync(vbasedev); - remain = migration->stopcopy_size; + } + + precopy_size = + migration->precopy_init_size + migration->precopy_dirty_size; + + if (migration->stopcopy_size > precopy_size) { + stopcopy_size = migration->stopcopy_size - precopy_size; } else { - if (!vfio_device_state_is_precopy(vbasedev)) { - return; - } - remain = migration->precopy_init_size + migration->precopy_dirty_size; + stopcopy_size = 0; } - pending->precopy_bytes += remain; + pending->precopy_bytes += precopy_size; + pending->stopcopy_bytes += stopcopy_size; trace_vfio_state_pending(vbasedev->name, migration->stopcopy_size, migration->precopy_init_size, -- 2.53.0
It used to hide in RAM dirty sync path. Now with more modules being able to slow sync on dirty information, keeping it there may not be good anymore because it's not RAM's own concept for iterations: all modules should follow. More importantly, mgmt may try to query dirty info (to make policy decisions like adjusting downtime) by listening to iteration count changes via QMP events. So we must make sure the boost of iterations only happens _after_ the dirty sync operations with whatever form (RAM's dirty bitmap sync, or VFIO's different ioctls to fetch latest dirty info from kernel). Move this to core migration path to manage, together with the event generation, so that it can be well ordered with the sync operations for all modules. This brings a good side effect that we should have an old issue regarding to cpu_throttle_dirty_sync_timer_tick() which can randomly boost iteration counts (because it invokes sync ops). Now it won't, which is actually the right behavior. Said that, we have code (not only QEMU, but likely mgmt too) assuming the 1st iteration will always shows dirty count to 1. Make it initialized with 1 this time, because we'll miss the dirty sync for setup() on boosting this counter now. Reviewed-by: Hyman Huang <yong.huang@smartx.com> Reviewed-by: Prasad Pandit <pjp@fedoraproject.org> Reviewed-by: Juraj Marcin <jmarcin@redhat.com> Link: https://lore.kernel.org/r/20260421202110.306051-10-peterx@redhat.com Signed-off-by: Peter Xu <peterx@redhat.com> --- migration/migration-stats.h | 3 ++- migration/migration.c | 29 ++++++++++++++++++++++++++--- migration/ram.c | 6 ------ 3 files changed, 28 insertions(+), 10 deletions(-) diff --git a/migration/migration-stats.h b/migration/migration-stats.h index XXXXXXX..XXXXXXX 100644 --- a/migration/migration-stats.h +++ b/migration/migration-stats.h @@ -XXX,XX +XXX,XX @@ typedef struct { */ uint64_t dirty_pages_rate; /* - * Number of times we have synchronized guest bitmaps. + * Number of times we have synchronized guest bitmaps. This always + * starts from 1 for the 1st iteration. */ uint64_t dirty_sync_count; /* diff --git a/migration/migration.c b/migration/migration.c index XXXXXXX..XXXXXXX 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -XXX,XX +XXX,XX @@ int migrate_init(MigrationState *s, Error **errp) s->threshold_size = 0; s->switchover_acked = false; s->rdma_migration = false; + /* - * set mig_stats memory to zero for a new migration + * set mig_stats memory to zero for a new migration.. except the + * iteration counter, which we want to make sure it returns 1 for the + * first iteration. */ memset(&mig_stats, 0, sizeof(mig_stats)); + mig_stats.dirty_sync_count = 1; + migration_reset_vfio_bytes_transferred(); s->postcopy_package_loaded = false; @@ -XXX,XX +XXX,XX @@ static bool migration_iteration_next_ready(MigrationState *s, static void migration_iteration_go_next(MigPendingData *pending) { /* - * Do a slow sync will achieve this. TODO: move RAM iteration code - * into the core layer. + * Do a slow sync first before boosting the iteration count. */ qemu_savevm_query_pending(pending, true); + + /* + * Boost dirty sync count to reflect we finished one iteration. + * + * NOTE: we need to make sure when this happens (together with the + * event sent below) all modules have slow-synced the pending data + * above. That means a write mem barrier, but qatomic_add() should be + * enough. + * + * It's because a mgmt could wait on the iteration event to query again + * on pending data for policy changes (e.g. downtime adjustments). The + * ordering will make sure the query will fetch the latest results from + * all the modules. + */ + qatomic_add(&mig_stats.dirty_sync_count, 1); + + if (migrate_events()) { + qapi_event_send_migration_pass(mig_stats.dirty_sync_count); + } } static bool postcopy_should_start(MigrationState *s, MigPendingData *pending) diff --git a/migration/ram.c b/migration/ram.c index XXXXXXX..XXXXXXX 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -XXX,XX +XXX,XX @@ static void migration_bitmap_sync(RAMState *rs, bool last_stage) RAMBlock *block; int64_t end_time; - qatomic_add(&mig_stats.dirty_sync_count, 1); - if (!rs->time_last_bitmap_sync) { rs->time_last_bitmap_sync = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); } @@ -XXX,XX +XXX,XX @@ static void migration_bitmap_sync(RAMState *rs, bool last_stage) rs->num_dirty_pages_period = 0; rs->bytes_xfer_prev = migration_transferred_bytes(); } - if (migrate_events()) { - uint64_t generation = qatomic_read(&mig_stats.dirty_sync_count); - qapi_event_send_migration_pass(generation); - } } void migration_bitmap_sync_precopy(bool last_stage) -- 2.53.0
Add a helper migration_get_switchover_bw() to return an estimate of switchover bandwidth. Use it to simplify the current code. This will be used in later to remove expected_downtime. When at it, remove two qatomic_read() to shrink the lines because atomic ops are not needed when it's always the same thread who does the updates. Reviewed-by: Juraj Marcin <jmarcin@redhat.com> Link: https://lore.kernel.org/r/20260421202110.306051-11-peterx@redhat.com Signed-off-by: Peter Xu <peterx@redhat.com> --- migration/migration.c | 48 +++++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index XXXXXXX..XXXXXXX 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -XXX,XX +XXX,XX @@ void migrate_send_rp_resume_ack(MigrationIncomingState *mis, uint32_t value) migrate_send_rp_message(mis, MIG_RP_MSG_RESUME_ACK, sizeof(buf), &buf); } +/* + * Returns the estimated switchover bandwidth (unit: bytes / seconds) + */ +static double migration_get_switchover_bw(MigrationState *s) +{ + uint64_t switchover_bw = migrate_avail_switchover_bandwidth(); + + if (switchover_bw) { + /* If user specified, prioritize this value and don't estimate */ + return (double)switchover_bw; + } + + return s->mbps / 8 * 1000 * 1000; +} + bool migration_is_running(void) { MigrationState *s = current_migration; @@ -XXX,XX +XXX,XX @@ static void migration_update_counters(MigrationState *s, { uint64_t transferred, transferred_pages, time_spent; uint64_t current_bytes; /* bytes transferred since the beginning */ - uint64_t switchover_bw; - /* Expected bandwidth when switching over to destination QEMU */ - double expected_bw_per_ms; - double bandwidth; + double switchover_bw_per_ms; if (current_time < s->iteration_start_time + BUFFER_DELAY) { return; } - switchover_bw = migrate_avail_switchover_bandwidth(); current_bytes = migration_transferred_bytes(); transferred = current_bytes - s->iteration_initial_bytes; time_spent = current_time - s->iteration_start_time; - bandwidth = (double)transferred / time_spent; - - if (switchover_bw) { - /* - * If the user specified a switchover bandwidth, let's trust the - * user so that can be more accurate than what we estimated. - */ - expected_bw_per_ms = (double)switchover_bw / 1000; - } else { - /* If the user doesn't specify bandwidth, we use the estimated */ - expected_bw_per_ms = bandwidth; - } - - s->threshold_size = expected_bw_per_ms * migrate_downtime_limit(); - s->mbps = (((double) transferred * 8.0) / ((double) time_spent / 1000.0)) / 1000.0 / 1000.0; + /* NOTE: only update this after bandwidth (s->mbps) updated */ + switchover_bw_per_ms = migration_get_switchover_bw(s) / 1000; + s->threshold_size = switchover_bw_per_ms * migrate_downtime_limit(); + transferred_pages = ram_get_total_transferred_pages() - s->iteration_initial_pages; s->pages_per_second = (double) transferred_pages / @@ -XXX,XX +XXX,XX @@ static void migration_update_counters(MigrationState *s, * if we haven't sent anything, we don't want to * recalculate. 10000 is a small enough number for our purposes */ - if (qatomic_read(&mig_stats.dirty_pages_rate) && - transferred > 10000) { + if (mig_stats.dirty_pages_rate && transferred > 10000) { s->expected_downtime = - qatomic_read(&mig_stats.dirty_bytes_last_sync) / expected_bw_per_ms; + mig_stats.dirty_bytes_last_sync / switchover_bw_per_ms; } migration_rate_reset(); @@ -XXX,XX +XXX,XX @@ static void migration_update_counters(MigrationState *s, trace_migrate_transferred(transferred, time_spent, /* Both in unit bytes/ms */ - bandwidth, switchover_bw / 1000, + (uint64_t)s->mbps, + (uint64_t)switchover_bw_per_ms, s->threshold_size); } -- 2.53.0
This value does not need to be calculated as frequent. Only calculate it on demand when query-migrate happened. With that we can remove the variable in MigrationState. This paves way for fixing this value to include all modules (not only RAM but others too). Reviewed-by: Juraj Marcin <jmarcin@redhat.com> Link: https://lore.kernel.org/r/20260421202110.306051-12-peterx@redhat.com Signed-off-by: Peter Xu <peterx@redhat.com> --- migration/migration.h | 2 +- migration/migration.c | 25 ++++++++++++------------- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/migration/migration.h b/migration/migration.h index XXXXXXX..XXXXXXX 100644 --- a/migration/migration.h +++ b/migration/migration.h @@ -XXX,XX +XXX,XX @@ struct MigrationState { /* Timestamp when VM is down (ms) to migrate the last stuff */ int64_t downtime_start; int64_t downtime; - int64_t expected_downtime; bool capabilities[MIGRATION_CAPABILITY__MAX]; int64_t setup_time; @@ -XXX,XX +XXX,XX @@ void migration_cancel(void); void migration_populate_vfio_info(MigrationInfo *info); void migration_reset_vfio_bytes_transferred(void); void postcopy_temp_page_reset(PostcopyTmpPage *tmp_page); +int64_t migration_downtime_calc_expected(MigrationState *s); /* * Migration thread waiting for return path thread. Return non-zero if an diff --git a/migration/migration.c b/migration/migration.c index XXXXXXX..XXXXXXX 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -XXX,XX +XXX,XX @@ static bool migrate_show_downtime(MigrationState *s) return (s->state == MIGRATION_STATUS_COMPLETED) || migration_in_postcopy(); } +/* Return expected downtime (unit: milliseconds) */ +int64_t migration_downtime_calc_expected(MigrationState *s) +{ + if (mig_stats.dirty_sync_count <= 1) { + return migrate_downtime_limit(); + } + + return mig_stats.dirty_bytes_last_sync / + migration_get_switchover_bw(s) * 1000; +} + static void populate_time_info(MigrationInfo *info, MigrationState *s) { info->has_status = true; @@ -XXX,XX +XXX,XX @@ static void populate_time_info(MigrationInfo *info, MigrationState *s) info->downtime = s->downtime; } else { info->has_expected_downtime = true; - info->expected_downtime = s->expected_downtime; + info->expected_downtime = migration_downtime_calc_expected(s); } } @@ -XXX,XX +XXX,XX @@ int migrate_init(MigrationState *s, Error **errp) s->mbps = 0.0; s->pages_per_second = 0.0; s->downtime = 0; - s->expected_downtime = 0; s->setup_time = 0; s->start_postcopy = false; s->migration_thread_running = false; @@ -XXX,XX +XXX,XX @@ static void migration_update_counters(MigrationState *s, s->pages_per_second = (double) transferred_pages / (((double) time_spent / 1000.0)); - /* - * if we haven't sent anything, we don't want to - * recalculate. 10000 is a small enough number for our purposes - */ - if (mig_stats.dirty_pages_rate && transferred > 10000) { - s->expected_downtime = - mig_stats.dirty_bytes_last_sync / switchover_bw_per_ms; - } - migration_rate_reset(); update_iteration_initial_status(s); @@ -XXX,XX +XXX,XX @@ void migration_start_outgoing(MigrationState *s) bool resume = (s->state == MIGRATION_STATUS_POSTCOPY_RECOVER_SETUP); int ret; - s->expected_downtime = migrate_downtime_limit(); - if (resume) { /* This is a resumed migration */ rate_limit = migrate_max_postcopy_bandwidth(); -- 2.53.0
QEMU will provide an expected downtime for the whole system during migration, by remembering the total dirty RAM that we synced the last time, divides the estimated switchover bandwidth. That was flawed when VFIO is taking into account: consider there is a VFIO GPU device that contains GBs of data to migrate during stop phase. Those will not be accounted in this math. Fix it by updating dirty_bytes_last_sync properly only when we go to the next iteration, rather than hide this update in the RAM code. Meanwhile, fetch the total (rather than RAM-only) portion of dirty bytes, so as to include GPU device states too. Update the comment of the field to reflect its new meaning. Now after this change, the expected-downtime to be read from query-migrate should be very accurate even with VFIO devices involved. Tested-by: Cédric Le Goater <clg@redhat.com> Reviewed-by: Juraj Marcin <jmarcin@redhat.com> Link: https://lore.kernel.org/r/20260421202110.306051-13-peterx@redhat.com Signed-off-by: Peter Xu <peterx@redhat.com> --- migration/migration-stats.h | 8 +++----- migration/migration.c | 11 ++++++++--- migration/ram.c | 1 - 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/migration/migration-stats.h b/migration/migration-stats.h index XXXXXXX..XXXXXXX 100644 --- a/migration/migration-stats.h +++ b/migration/migration-stats.h @@ -XXX,XX +XXX,XX @@ */ typedef struct { /* - * Number of bytes that were dirty last time that we synced with - * the guest memory. We use that to calculate the downtime. As - * the remaining dirty amounts to what we know that is still dirty - * since last iteration, not counting what the guest has dirtied - * since we synchronized bitmaps. + * Number of bytes that were reported dirty after the latest + * system-wise synchronization of dirty information. It is used to do + * best-effort estimation on expected downtime. */ uint64_t dirty_bytes_last_sync; /* diff --git a/migration/migration.c b/migration/migration.c index XXXXXXX..XXXXXXX 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -XXX,XX +XXX,XX @@ static void migration_iteration_go_next(MigPendingData *pending) */ qemu_savevm_query_pending(pending, true); + /* + * Update the dirty information for the whole system for this + * iteration. This value is used to calculate expected downtime. + */ + qatomic_set(&mig_stats.dirty_bytes_last_sync, pending->total_bytes); + /* * Boost dirty sync count to reflect we finished one iteration. * * NOTE: we need to make sure when this happens (together with the * event sent below) all modules have slow-synced the pending data - * above. That means a write mem barrier, but qatomic_add() should be - * enough. + * above and updated corresponding fields (e.g. dirty_bytes_last_sync). * * It's because a mgmt could wait on the iteration event to query again * on pending data for policy changes (e.g. downtime adjustments). The * ordering will make sure the query will fetch the latest results from - * all the modules. + * all the modules on everything. */ qatomic_add(&mig_stats.dirty_sync_count, 1); diff --git a/migration/ram.c b/migration/ram.c index XXXXXXX..XXXXXXX 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -XXX,XX +XXX,XX @@ static void migration_bitmap_sync(RAMState *rs, bool last_stage) RAMBLOCK_FOREACH_NOT_IGNORED(block) { ramblock_sync_dirty_bitmap(rs, block); } - qatomic_set(&mig_stats.dirty_bytes_last_sync, ram_bytes_remaining()); } } -- 2.53.0
Introduce this new counter to remember the total dirty bytes for the whole system. It will be used for query-migrate command to fetch system-wise remaining data. A prior attempt was made to not use this counter but query directly from all the modules in a QMP handler, but it exposed some complexity not only on migration state machine race conditions (where the query may be invoked anytime of the state machine), or on locking implications (where some of the query hooks may take BQL, which is illegal at least in a QMP handler). For more information, see: https://lore.kernel.org/r/aeZMtxqrKWAMKzdN@x1.local This oneliner will resolve everything, except that it is not as accurate. The hope is it is a worthwhile trade-off solution, after knowing above challenges. Now, there is one more reason we should make each invocation of save_live_iterate() to be lightweight, because this counter will only get updated once for each loop over all save_live_iterate() hooks when present. But that's always the goal. Reviewed-by: Juraj Marcin <jmarcin@redhat.com> Link: https://lore.kernel.org/r/20260421202110.306051-14-peterx@redhat.com Signed-off-by: Peter Xu <peterx@redhat.com> --- migration/migration-stats.h | 7 +++++++ migration/savevm.c | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/migration/migration-stats.h b/migration/migration-stats.h index XXXXXXX..XXXXXXX 100644 --- a/migration/migration-stats.h +++ b/migration/migration-stats.h @@ -XXX,XX +XXX,XX @@ typedef struct { * best-effort estimation on expected downtime. */ uint64_t dirty_bytes_last_sync; + /* + * Number of bytes that were reported dirty now. This is an estimate + * value and will be updated every time migration thread queries from + * modules in an iteration loop. It is used to provide best-effort + * estimation on total remaining data. + */ + uint64_t dirty_bytes_total; /* * Number of pages dirtied per second. */ diff --git a/migration/savevm.c b/migration/savevm.c index XXXXXXX..XXXXXXX 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -XXX,XX +XXX,XX @@ void qemu_savevm_query_pending(MigPendingData *pending, bool exact) pending->total_bytes = pending->precopy_bytes + pending->stopcopy_bytes + pending->postcopy_bytes; + /* + * Update system remaining dirty bytes whenever QEMU queries. It will + * make the value to be not as accurate, but should still be pretty + * close to reality when this got invoked frequently while iterating. + */ + mig_stats.dirty_bytes_total = pending->total_bytes; + trace_qemu_savevm_query_pending(exact, pending->precopy_bytes, pending->stopcopy_bytes, pending->postcopy_bytes, -- 2.53.0
Currently, mgmt can only query for remaining RAM using QMP command "query-migrate" and monitor the "ram" section. There's no way to report system-wide remaining data including VFIO devices. It was not a problem before, because for a very long time RAM was the only part that matters. After VFIO migrations landed upstream, it may not be enough. There can be GPU devices that contain GBs of device states. Mgmt may want to know how much remaining for special devices like VFIO, because all of them will be accounted as VM data to migrate and will contribute to downtime in the switchover phase. Add a new "remaining" field in query-migrate results on the top level, reflecting system-wide remaining data, which will include everything like VFIO devices. This information will be useful for mgmt to implement generic way of stall detection that covers all system resources. For example, when system-wide remaining data (especially, if sampled right after each migration iteration) does not decrease anymore for a relatively long period of time, then it may imply there is a challenge of converging, mgmt can react based on how this value changes over time. Before this patch, "expected_downtime" almost played this role. For example, by monitoring "expected_downtime" at the beginning of each iteration can in most cases also reflect the progress of migration system-wide. Said that, "expected_downtime" was always calculated based on a bandwidth value that can fluctuate if avail-switchover-bandwidth is not used. This new "remaining" field will remove that part of uncertainty for mgmt no matter if avail-switchover-bandwidth is used by the mgmt. With the new field, HMP "info migrate" now reports this: (qemu) info migrate Status: active Time (ms): total=12080, setup=14, exp_down=300 Remaining: 1.36 GiB <--- this is the new line RAM info: Throughput (Mbps): 840.50 Sizes: pagesize=4 KiB, total=4.02 GiB Transfers: transferred=1.18 GiB, remain=1.36 GiB Channels: precopy=1.18 GiB, multifd=0 B, postcopy=0 B Page Types: normal=307923, zero=388148 Page Rates (pps): transfer=25660 Others: dirty_syncs=1 When VFIO is not involved, the value reported in the new field should be approximately the same as reported in the "remaining" field of the RAM section. It is only approximately because the system-wide remaining data is a cached value, which gets frequently updated by migration core. OTOH, the RAM's remaining data is accurate. When VFIO is involved, the new value reported should normally be larger, because it will include the size of VFIO remaining data too. Cc: Aseef Imran <aimran@redhat.com> Reviewed-by: Juraj Marcin <jmarcin@redhat.com> Reviewed-by: Dr. David Alan Gilbert <dave@treblig.org> Acked-by: Markus Armbruster <armbru@redhat.com> # QAPI schema Link: https://lore.kernel.org/r/20260421202110.306051-15-peterx@redhat.com Signed-off-by: Peter Xu <peterx@redhat.com> --- qapi/migration.json | 4 ++++ migration/migration-hmp-cmds.c | 5 +++++ migration/migration.c | 7 +++++++ 3 files changed, 16 insertions(+) diff --git a/qapi/migration.json b/qapi/migration.json index XXXXXXX..XXXXXXX 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -XXX,XX +XXX,XX @@ # average memory load of the virtual CPU indirectly. Note that # zero means guest doesn't dirty memory. (Since 8.1) # +# @remaining: amount of bytes remaining to be migrated system-wide, +# includes both RAM and all devices (like VFIO). (Since 11.1) +# # Features: # # @unstable: Members @postcopy-latency, @postcopy-vcpu-latency, @@ -XXX,XX +XXX,XX @@ ## { 'struct': 'MigrationInfo', 'data': {'*status': 'MigrationStatus', '*ram': 'MigrationRAMStats', + '*remaining': 'size', '*vfio': 'VfioStats', '*xbzrle-cache': 'XBZRLECacheStats', '*total-time': 'int', diff --git a/migration/migration-hmp-cmds.c b/migration/migration-hmp-cmds.c index XXXXXXX..XXXXXXX 100644 --- a/migration/migration-hmp-cmds.c +++ b/migration/migration-hmp-cmds.c @@ -XXX,XX +XXX,XX @@ void hmp_info_migrate(Monitor *mon, const QDict *qdict) } } + if (info->has_remaining) { + g_autofree char *remaining = size_to_str(info->remaining); + monitor_printf(mon, "Remaining: \t\t%s\n", remaining); + } + if (info->has_socket_address) { SocketAddressList *addr; diff --git a/migration/migration.c b/migration/migration.c index XXXXXXX..XXXXXXX 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -XXX,XX +XXX,XX @@ static void populate_time_info(MigrationInfo *info, MigrationState *s) } } +static void populate_global_info(MigrationInfo *info, MigrationState *s) +{ + info->has_remaining = true; + info->remaining = qatomic_read(&mig_stats.dirty_bytes_total); +} + static void populate_ram_info(MigrationInfo *info, MigrationState *s) { size_t page_size = qemu_target_page_size(); @@ -XXX,XX +XXX,XX @@ static void fill_source_migration_info(MigrationInfo *info) /* TODO add some postcopy stats */ populate_time_info(info, s); populate_ram_info(info, s); + populate_global_info(info, s); migration_populate_vfio_info(info); break; case MIGRATION_STATUS_COLO: -- 2.53.0
Add ", in bytes per second". Unfortunately indentations need to be updated completely, but no change on the rest. Cc: Markus Armbruster <armbru@redhat.com> Suggested-by: Juraj Marcin <jmarcin@redhat.com> Reviewed-by: Juraj Marcin <jmarcin@redhat.com> Acked-by: Markus Armbruster <armbru@redhat.com> Link: https://lore.kernel.org/r/20260421202110.306051-16-peterx@redhat.com Signed-off-by: Peter Xu <peterx@redhat.com> --- qapi/migration.json | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/qapi/migration.json b/qapi/migration.json index XXXXXXX..XXXXXXX 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -XXX,XX +XXX,XX @@ # (Since 2.8) # # @avail-switchover-bandwidth: to set the available bandwidth that -# migration can use during switchover phase. **Note:** this does -# not limit the bandwidth during switchover, but only for -# calculations when making decisions to switchover. By default, -# this value is zero, which means QEMU will estimate the bandwidth -# automatically. This can be set when the estimated value is not -# accurate, while the user is able to guarantee such bandwidth is -# available when switching over. When specified correctly, this -# can make the switchover decision much more accurate. -# (Since 8.2) +# migration can use during switchover phase, in bytes per +# second. **Note:** this does not limit the bandwidth during +# switchover, but only for calculations when making decisions to +# switchover. By default, this value is zero, which means QEMU +# will estimate the bandwidth automatically. This can be set +# when the estimated value is not accurate, while the user is +# able to guarantee such bandwidth is available when switching +# over. When specified correctly, this can make the switchover +# decision much more accurate. (Since 8.2) # # @downtime-limit: set maximum tolerated downtime for migration. # maximum downtime in milliseconds (Since 2.8) -- 2.53.0
Add two tracepoints for both precopy and stopcopy query ioctls. When at it, add one warn_report_once() for each of them when it fails. Reviewed-by: Avihai Horon <avihaih@nvidia.com> Tested-by: Cédric Le Goater <clg@redhat.com> Reviewed-by: Cédric Le Goater <clg@redhat.com> Link: https://lore.kernel.org/r/20260421202110.306051-17-peterx@redhat.com Signed-off-by: Peter Xu <peterx@redhat.com> --- hw/vfio/migration.c | 35 +++++++++++++++++++++++++---------- hw/vfio/trace-events | 2 ++ 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/hw/vfio/migration.c b/hw/vfio/migration.c index XXXXXXX..XXXXXXX 100644 --- a/hw/vfio/migration.c +++ b/hw/vfio/migration.c @@ -XXX,XX +XXX,XX @@ static int vfio_query_stop_copy_size(VFIODevice *vbasedev) struct vfio_device_feature_mig_data_size *mig_data_size = (struct vfio_device_feature_mig_data_size *)feature->data; VFIOMigration *migration = vbasedev->migration; + int ret; feature->argsz = sizeof(buf); feature->flags = @@ -XXX,XX +XXX,XX @@ static int vfio_query_stop_copy_size(VFIODevice *vbasedev) * is reported so downtime limit won't be violated. */ migration->stopcopy_size = VFIO_MIG_STOP_COPY_SIZE; - return -errno; + ret = -errno; + warn_report_once("VFIO device %s ioctl(VFIO_DEVICE_FEATURE) on " + "VFIO_DEVICE_FEATURE_MIG_DATA_SIZE failed (%d)", + vbasedev->name, ret); + } else { + migration->stopcopy_size = mig_data_size->stop_copy_length; + ret = 0; } - migration->stopcopy_size = mig_data_size->stop_copy_length; + trace_vfio_query_stop_copy_size(vbasedev->name, + migration->stopcopy_size, ret); - return 0; + return ret; } static int vfio_query_precopy_size(VFIOMigration *migration) @@ -XXX,XX +XXX,XX @@ static int vfio_query_precopy_size(VFIOMigration *migration) struct vfio_precopy_info precopy = { .argsz = sizeof(precopy), }; - - migration->precopy_init_size = 0; - migration->precopy_dirty_size = 0; + int ret; if (ioctl(migration->data_fd, VFIO_MIG_GET_PRECOPY_INFO, &precopy)) { - return -errno; + migration->precopy_init_size = 0; + migration->precopy_dirty_size = 0; + ret = -errno; + warn_report_once("VFIO device %s ioctl(VFIO_MIG_GET_PRECOPY_INFO) " + "failed (%d)", migration->vbasedev->name, ret); + } else { + migration->precopy_init_size = precopy.initial_bytes; + migration->precopy_dirty_size = precopy.dirty_bytes; + ret = 0; } - migration->precopy_init_size = precopy.initial_bytes; - migration->precopy_dirty_size = precopy.dirty_bytes; + trace_vfio_query_precopy_size(migration->vbasedev->name, + migration->precopy_init_size, + migration->precopy_dirty_size, ret); - return 0; + return ret; } /* Returns the size of saved data on success and -errno on error */ diff --git a/hw/vfio/trace-events b/hw/vfio/trace-events index XXXXXXX..XXXXXXX 100644 --- a/hw/vfio/trace-events +++ b/hw/vfio/trace-events @@ -XXX,XX +XXX,XX @@ vfio_migration_realize(const char *name) " (%s)" vfio_migration_set_device_state(const char *name, const char *state) " (%s) state %s" vfio_migration_set_state(const char *name, const char *new_state, const char *recover_state) " (%s) new state %s, recover state %s" vfio_migration_state_notifier(const char *name, int state) " (%s) state %d" +vfio_query_precopy_size(const char *name, uint64_t init_size, uint64_t dirty_size, int ret) " (%s) init %"PRIu64" dirty %"PRIu64" ret %d" +vfio_query_stop_copy_size(const char *name, uint64_t size, int ret) " (%s) stopcopy size %"PRIu64" ret %d" vfio_save_block(const char *name, int data_size) " (%s) data_size %d" vfio_save_block_precopy_empty_hit(const char *name) " (%s)" vfio_save_cleanup(const char *name) " (%s)" -- 2.53.0
From: CJ Chen <cjchen@igel.co.jp> The riscv_iommu_trap_ops MemoryRegionOps specifies that unaligned accesses are not valid for this device but that it does implement them. This doesn't make much sense, and we want to add an assertion that registered MRs don't specify this invalid combination of settings. Drop .impl.unaligned = true, with no behaviour change. Signed-off-by: CJ Chen <cjchen@igel.co.jp> Acked-by: Tomoyuki Hirose <hrstmyk811m@gmail.com> Reported-by: Tomoyuki Hirose <hrstmyk811m@gmail.com> Reviewed-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> [PMM: reworded commit message] Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Alistair Francis <alistair.francis@wdc.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Link: https://lore.kernel.org/r/20260428093339.2087081-2-peter.maydell@linaro.org Signed-off-by: Peter Xu <peterx@redhat.com> --- hw/riscv/riscv-iommu.c | 1 - 1 file changed, 1 deletion(-) diff --git a/hw/riscv/riscv-iommu.c b/hw/riscv/riscv-iommu.c index XXXXXXX..XXXXXXX 100644 --- a/hw/riscv/riscv-iommu.c +++ b/hw/riscv/riscv-iommu.c @@ -XXX,XX +XXX,XX @@ static const MemoryRegionOps riscv_iommu_trap_ops = { .impl = { .min_access_size = 4, .max_access_size = 8, - .unaligned = true, }, .valid = { .min_access_size = 4, -- 2.53.0
From: Peter Maydell <peter.maydell@linaro.org> Currently npcm7xx_fiu_flash_ops provides no .impl substruct; this means that it gets the default of "implements 1, 2 and 4 byte aligned accesses". This is more constrained than the device permits in its .valid substruct, and also narrower than the functions are written to handle. Add a .impl substruct matching the .valid substruct; this means that all guest accesses are handled directly by the read and write functions, and are never synthesized by the memory subsystem performing multiple accesses to the device (which would not behave correctly, as these read and write fucntions have side effects). Based-on-a-patch-by: CJ Chen <cjchen@igel.co.jp> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Link: https://lore.kernel.org/r/20260428093339.2087081-3-peter.maydell@linaro.org Signed-off-by: Peter Xu <peterx@redhat.com> --- hw/ssi/npcm7xx_fiu.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/hw/ssi/npcm7xx_fiu.c b/hw/ssi/npcm7xx_fiu.c index XXXXXXX..XXXXXXX 100644 --- a/hw/ssi/npcm7xx_fiu.c +++ b/hw/ssi/npcm7xx_fiu.c @@ -XXX,XX +XXX,XX @@ static const MemoryRegionOps npcm7xx_fiu_flash_ops = { .read = npcm7xx_fiu_flash_read, .write = npcm7xx_fiu_flash_write, .endianness = DEVICE_LITTLE_ENDIAN, + .impl = { + .min_access_size = 1, + .max_access_size = 8, + .unaligned = true, + }, .valid = { .min_access_size = 1, .max_access_size = 8, -- 2.53.0
From: Peter Maydell <peter.maydell@linaro.org> The xtensa mx-pic interrupt controller has a rather odd register setup, where some registers are 32 bits but are decoded at offsets only one apart from each other. The QEMU implementation handles this correctly, but it did not set .impl.unaligned = true. This has worked up til now because QEMU has entirely ignored .impl.unaligned, and just allowed through unaligned accesses when .valid.unaligned is set. To allow the possibility of properly implementing synthesis of unaligned accesses by the memory subsystem when they are valid but the device doesn't implement them, and for clarity of intention, state explicitly that this MR's read and write functions directly handle unaligned accesses, by setting .impl.unaligned = true. While we are adjusting the MemoryRegionOps, we set also the minimum and maximum allowed access sizes. Since the only way to get at this device is via the CPU's RER and WER instructions, which always operate at 32-bit sizes (see the HELPER(rer) and HELPER(wer) functions in target/xtensa/op_helper.c), we know we will always get 32-bit accesses. Specify explicitly that that is what is valid and implemented for the MR. Add a comment to clarify that the hardware behaviour here is not "true memory-mapped registers", so the odd-looking implementation is correct. Based-on-a-patch-by: CJ Chen <cjchen@igel.co.jp> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Max Filippov <jcmvbkbc@gmail.com> Link: https://lore.kernel.org/r/20260428093339.2087081-4-peter.maydell@linaro.org Signed-off-by: Peter Xu <peterx@redhat.com> --- hw/xtensa/mx_pic.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/hw/xtensa/mx_pic.c b/hw/xtensa/mx_pic.c index XXXXXXX..XXXXXXX 100644 --- a/hw/xtensa/mx_pic.c +++ b/hw/xtensa/mx_pic.c @@ -XXX,XX +XXX,XX @@ struct XtensaMxPic { } cpu[MX_MAX_CPU]; }; +/* + * Note that decode for these registers is rather strange by the usual + * MMIO standards -- the MIROUT and MIPICAUSE areas can be read and + * written at 32-bit length, returning different values for each byte + * offset, because the low bits of the address are treated as selecting + * an IRQ or a processor: + * + * 00nn 0...0p..p Interrupt Routing, route IRQ n to processor p + * 01pp 0...0d..d 16 bits (d) 'ored' as single IPI to processor p + * + * This is because (like x86 IO port in/out accesses) the offset is + * not a memory-mapped address but is really a register number, + * accessed via the Xtensa RER/WER "external register" instructions. + * + * We set .valid and .impl to both allow unaligned = true to permit + * these byte-offsets. Because this device is not a true memory mapped + * device but is accessible only via the Xtensa RER/WER "external + * register" interface, all accesses are guaranteed 32 bits. + */ + static uint64_t xtensa_mx_pic_ext_reg_read(void *opaque, hwaddr offset, unsigned size) { @@ -XXX,XX +XXX,XX @@ static const MemoryRegionOps xtensa_mx_pic_ops = { .read = xtensa_mx_pic_ext_reg_read, .write = xtensa_mx_pic_ext_reg_write, .endianness = DEVICE_NATIVE_ENDIAN, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + .unaligned = true, + }, .valid = { + .min_access_size = 4, + .max_access_size = 4, .unaligned = true, }, }; -- 2.53.0
From: CJ Chen <cjchen@igel.co.jp> When it comes to this pattern: .valid.unaligned = false and impl.unaligned = true, is effectlvely contradictory. The .valid structure indicates that unaligned access should be rejected at the access validation phase, yet .impl suggests the underlying device implementation can handle unaligned operations. As a result, the upper-layer code will never even reach the .impl logic. Add an assertion that the MemoryRegionOps doesn't specify this invalid combination. Signed-off-by: CJ Chen <cjchen@igel.co.jp> Tested-by: CJ Chen <cjchen@igel.co.jp> Suggested-by: Peter Xu <peterx@redhat.com> Acked-by: Tomoyuki Hirose <hrstmyk811m@gmail.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> [PMM: tweaked commit message] Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Link: https://lore.kernel.org/r/20260428093339.2087081-5-peter.maydell@linaro.org Signed-off-by: Peter Xu <peterx@redhat.com> --- system/memory.c | 1 + 1 file changed, 1 insertion(+) diff --git a/system/memory.c b/system/memory.c index XXXXXXX..XXXXXXX 100644 --- a/system/memory.c +++ b/system/memory.c @@ -XXX,XX +XXX,XX @@ void memory_region_init_io(MemoryRegion *mr, Object *owner, const MemoryRegionOps *ops, void *opaque, const char *name, uint64_t size) { + g_assert(!ops || !(ops->impl.unaligned && !ops->valid.unaligned)); memory_region_init(mr, owner, name, size); memory_region_set_ops(mr, ops, opaque); } -- 2.53.0
From: Fabiano Rosas <farosas@suse.de> The generation of the guest workloads for migration-test is all broken: 1) We moved code around and forgot to update the includes from the guest workloads at qtest/migration/<ARCH>/. 2) Since this code doesn't build by default due to the need for cross compilation, we also broke the s390x code entirely by removing the libc implementation from sclp.c. 3) The build adds a blank line at the end of the header files due to how the sed command used to remove extra variable declarations is written. 4) Must be too long since we last built this thing that powerpc compilers would output BE by default. Nowadays we need -mbig-endian to force it. 5) Format of comments needs fixing to please checkpatch. Fix everything. Most changes are trivial, except for s390x where I had to reimplement some of the routines from sclp.c. Compiling that object file in doesn't work because of its various includes that this standalone code can't provide. Testing this change (assuming x86 host): $ cd build $ pushd tests/qtest/migration $ make clean $ make i386 $ make CROSS_PREFIX=your-favorite-cross-compiler- s390x $ make CROSS_PREFIX=your-favorite-cross-compiler- aarch64 $ make CROSS_PREFIX=your-favorite-cross-compiler- ppc64 $ popd $ make -j$(nproc) # must build the tests again $ QTEST_QEMU_BINARY=./qemu-system-<ARCH> migration-test Fixes: 9f4278837d ("pc-bios/s390-ccw: Use the libc from SLOF and remove sclp prints") Fixes: e1803dabdc ("tests/qtest/migration: Move common test code") Signed-off-by: Fabiano Rosas <farosas@suse.de> Link: https://lore.kernel.org/r/20260429231025.30818-1-farosas@suse.de Signed-off-by: Peter Xu <peterx@redhat.com> --- tests/qtest/migration/aarch64/a-b-kernel.h | 7 +- tests/qtest/migration/bootfile.h | 11 + tests/qtest/migration/i386/a-b-bootblock.h | 8 +- tests/qtest/migration/ppc64/a-b-kernel.h | 8 +- tests/qtest/migration/s390x/a-b-bios.h | 611 +++++++++++++-------- tests/qtest/migration/s390x/a-b-bios.c | 66 ++- tests/qtest/migration/Makefile | 11 +- tests/qtest/migration/aarch64/Makefile | 4 +- tests/qtest/migration/aarch64/a-b-kernel.S | 2 +- tests/qtest/migration/i386/Makefile | 4 +- tests/qtest/migration/i386/a-b-bootblock.S | 2 +- tests/qtest/migration/ppc64/Makefile | 6 +- tests/qtest/migration/ppc64/a-b-kernel.S | 2 +- tests/qtest/migration/s390x/Makefile | 8 +- 14 files changed, 492 insertions(+), 258 deletions(-) diff --git a/tests/qtest/migration/aarch64/a-b-kernel.h b/tests/qtest/migration/aarch64/a-b-kernel.h index XXXXXXX..XXXXXXX 100644 --- a/tests/qtest/migration/aarch64/a-b-kernel.h +++ b/tests/qtest/migration/aarch64/a-b-kernel.h @@ -XXX,XX +XXX,XX @@ -/* This file is automatically generated from the assembly file in - * tests/migration/aarch64. Edit that file and then run "make all" - * inside tests/migration to update, and then remember to send both +/* + * This file is automatically generated from the assembly file in + * tests/qtest/migration/aarch64. Edit that file and then run "make" + * inside tests/qtest/migration to update, and then remember to send both * the header and the assembler differences in your patch submission. */ unsigned char aarch64_kernel[] = { diff --git a/tests/qtest/migration/bootfile.h b/tests/qtest/migration/bootfile.h index XXXXXXX..XXXXXXX 100644 --- a/tests/qtest/migration/bootfile.h +++ b/tests/qtest/migration/bootfile.h @@ -XXX,XX +XXX,XX @@ #ifndef BOOTFILE_H #define BOOTFILE_H +/* + * This file is included from qtest, but also from the assembly code + * that generates the header file containing the guest workload. Don't + * expose the function declarations and non-standard types to it. + */ +#ifdef __ASSEMBLER__ +#define MIGRATION_GUEST_CODE +#endif + /* Common */ #define TEST_MEM_PAGE_SIZE 4096 @@ -XXX,XX +XXX,XX @@ */ #define ARM_TEST_MAX_KERNEL_SIZE (512 * 1024) +#ifndef MIGRATION_GUEST_CODE void bootfile_delete(void); char *bootfile_create(const char *arch, const char *dir, bool suspend_me); char *bootfile_get(void); +#endif #endif /* BOOTFILE_H */ diff --git a/tests/qtest/migration/i386/a-b-bootblock.h b/tests/qtest/migration/i386/a-b-bootblock.h index XXXXXXX..XXXXXXX 100644 --- a/tests/qtest/migration/i386/a-b-bootblock.h +++ b/tests/qtest/migration/i386/a-b-bootblock.h @@ -XXX,XX +XXX,XX @@ -/* This file is automatically generated from the assembly file in - * tests/migration/i386. Edit that file and then run "make all" - * inside tests/migration to update, and then remember to send both +/* + * This file is automatically generated from the assembly file in + * tests/qtest/migration/i386. Edit that file and then run "make" + * inside tests/qtest/migration to update, and then remember to send both * the header and the assembler differences in your patch submission. */ unsigned char x86_bootsect[] = { @@ -XXX,XX +XXX,XX @@ unsigned char x86_bootsect[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0xaa }; - #define SYM_do_zero 0x00007c3d #define SYM_gdt 0x00007ca0 #define SYM_gdtdesc 0x00007cb8 diff --git a/tests/qtest/migration/ppc64/a-b-kernel.h b/tests/qtest/migration/ppc64/a-b-kernel.h index XXXXXXX..XXXXXXX 100644 --- a/tests/qtest/migration/ppc64/a-b-kernel.h +++ b/tests/qtest/migration/ppc64/a-b-kernel.h @@ -XXX,XX +XXX,XX @@ -/* This file is automatically generated from the assembly file in - * tests/migration/ppc64. Edit that file and then run "make all" - * inside tests/migration to update, and then remember to send both +/* + * This file is automatically generated from the assembly file in + * tests/qtest/migration/ppc64. Edit that file and then run "make" + * inside tests/qtest/migration to update, and then remember to send both * the header and the assembler differences in your patch submission. */ unsigned char ppc64_kernel[] = { @@ -XXX,XX +XXX,XX @@ unsigned char ppc64_kernel[] = { 0x38, 0xa0, 0x00, 0x01, 0x38, 0xc0, 0x00, 0x42, 0x78, 0xc6, 0xc1, 0xc6, 0x44, 0x00, 0x00, 0x22, 0x4b, 0xff, 0xff, 0xcc }; - diff --git a/tests/qtest/migration/s390x/a-b-bios.h b/tests/qtest/migration/s390x/a-b-bios.h index XXXXXXX..XXXXXXX 100644 --- a/tests/qtest/migration/s390x/a-b-bios.h +++ b/tests/qtest/migration/s390x/a-b-bios.h @@ -XXX,XX +XXX,XX @@ -/* This file is automatically generated from the a-b-bios.c file in - * tests/migration/s390x. Edit that file and then run "make all" - * inside tests/migration to update, and then remember to send both +/* + * This file is automatically generated from the assembly file in + * tests/qtest/migration/s390x. Edit that file and then run "make" + * inside tests/qtest/migration to update, and then remember to send both * the header and the assembler differences in your patch submission. */ unsigned char s390x_elf[] = { 0x7f, 0x45, 0x4c, 0x46, 0x02, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x16, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xa8, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x38, 0x00, 0x07, 0x00, 0x40, - 0x00, 0x0d, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, + 0x00, 0x10, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x88, 0x00, 0x00, 0x00, 0x00, @@ -XXX,XX +XXX,XX @@ unsigned char s390x_elf[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xac, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xac, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x88, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xb8, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x17, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0xb8, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x38, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x18, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0xd8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1e, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0xd8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x30, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x21, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x07, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0xb8, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0xb8, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x01, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x20, + 0x00, 0x00, 0x0e, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0xd8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0xd8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x64, 0x74, 0xe5, 0x51, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x64, 0x74, 0xe5, 0x52, 0x00, 0x00, 0x00, 0x04, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xb8, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x17, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0xb8, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x38, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x01, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0xd8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1e, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0xd8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x28, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x2f, 0x6c, 0x69, 0x62, 0x2f, 0x6c, 0x64, 0x36, 0x34, 0x2e, 0x73, 0x6f, - 0x2e, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x2e, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x02, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x03, 0xa8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0xf0, 0xeb, 0xef, 0xf0, 0x70, - 0x00, 0x24, 0xa7, 0xfb, 0xff, 0x60, 0xc0, 0xe5, 0x00, 0x00, 0x01, 0x5f, - 0xc0, 0x20, 0x00, 0x00, 0x02, 0xa8, 0xc0, 0xe5, 0x00, 0x00, 0x01, 0x75, - 0xa5, 0x2e, 0x00, 0x10, 0xa7, 0x19, 0x63, 0x00, 0x92, 0x00, 0x20, 0x00, - 0xa7, 0x2b, 0x10, 0x00, 0xa7, 0x17, 0xff, 0xfc, 0xa5, 0x1e, 0x00, 0x10, - 0xa7, 0x29, 0x63, 0x00, 0xe3, 0x30, 0x10, 0x00, 0x00, 0x90, 0xa7, 0x3a, - 0x00, 0x01, 0x42, 0x30, 0x10, 0x00, 0xa7, 0x1b, 0x10, 0x00, 0xa7, 0x27, - 0xff, 0xf7, 0xc0, 0x20, 0x00, 0x00, 0x02, 0x8a, 0xc0, 0xe5, 0x00, 0x00, - 0x01, 0x56, 0xa7, 0xf4, 0xff, 0xeb, 0x07, 0x07, 0xc0, 0xf0, 0x00, 0x00, - 0x4e, 0x5c, 0xc0, 0x20, 0x00, 0x00, 0x00, 0x7d, 0xe3, 0x20, 0x20, 0x00, - 0x00, 0x04, 0xc0, 0x30, 0x00, 0x00, 0x96, 0xa3, 0xb9, 0x0b, 0x00, 0x32, - 0xb9, 0x02, 0x00, 0x33, 0xa7, 0x84, 0x00, 0x19, 0xa7, 0x3b, 0xff, 0xff, - 0xeb, 0x43, 0x00, 0x08, 0x00, 0x0c, 0xb9, 0x02, 0x00, 0x44, 0xb9, 0x04, - 0x00, 0x12, 0xa7, 0x84, 0x00, 0x09, 0xd7, 0xff, 0x10, 0x00, 0x10, 0x00, - 0x41, 0x10, 0x11, 0x00, 0xa7, 0x47, 0xff, 0xfb, 0xc0, 0x20, 0x00, 0x00, - 0x00, 0x0d, 0x44, 0x30, 0x20, 0x00, 0xc0, 0x20, 0x00, 0x00, 0x00, 0x5b, - 0xd2, 0x0f, 0x01, 0xd0, 0x20, 0x00, 0xa7, 0xf4, 0xff, 0xa1, 0xd7, 0x00, - 0x10, 0x00, 0x10, 0x00, 0xc0, 0x10, 0x00, 0x00, 0x00, 0x50, 0xb2, 0xb2, - 0x10, 0x00, 0xa7, 0xf4, 0x00, 0x00, 0xeb, 0x00, 0xf0, 0x00, 0x00, 0x25, - 0x96, 0x02, 0xf0, 0x06, 0xeb, 0x00, 0xf0, 0x00, 0x00, 0x2f, 0xc0, 0x10, - 0x00, 0x00, 0x00, 0x2a, 0xe3, 0x10, 0x01, 0xb8, 0x00, 0x24, 0xc0, 0x10, - 0x00, 0x00, 0x00, 0x4b, 0xd2, 0x07, 0x01, 0xb0, 0x10, 0x00, 0xc0, 0x10, - 0x00, 0x00, 0x00, 0x3d, 0xb2, 0xb2, 0x10, 0x00, 0xeb, 0x66, 0xf0, 0x00, - 0x00, 0x25, 0x96, 0xff, 0xf0, 0x04, 0xeb, 0x66, 0xf0, 0x00, 0x00, 0x2f, - 0xc0, 0x10, 0x00, 0x00, 0x00, 0x1a, 0xe3, 0x10, 0x01, 0xf8, 0x00, 0x24, - 0xc0, 0x10, 0x00, 0x00, 0x00, 0x36, 0xd2, 0x07, 0x01, 0xf0, 0x10, 0x00, - 0xc0, 0x10, 0x00, 0x00, 0x00, 0x24, 0xb2, 0xb2, 0x10, 0x00, 0xeb, 0x00, - 0xf0, 0x00, 0x00, 0x25, 0x94, 0xfd, 0xf0, 0x06, 0xeb, 0x00, 0xf0, 0x00, - 0x00, 0x2f, 0x07, 0xfe, 0xeb, 0x66, 0xf0, 0x00, 0x00, 0x25, 0x94, 0x00, - 0xf0, 0x04, 0xeb, 0x66, 0xf0, 0x00, 0x00, 0x2f, 0x07, 0xfe, 0x07, 0x07, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0xf0, 0x00, 0x02, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x07, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x70, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, + 0xeb, 0xdf, 0xf0, 0x68, 0x00, 0x24, 0xc0, 0xd0, 0x00, 0x00, 0x01, 0x05, + 0xa7, 0xfb, 0xff, 0x60, 0xc0, 0x10, 0x00, 0x00, 0x96, 0xc0, 0xa7, 0x28, + 0x00, 0x1c, 0x40, 0x20, 0x10, 0x00, 0xa5, 0x2c, 0x00, 0x04, 0xe3, 0x20, + 0x10, 0x0a, 0x00, 0x24, 0xa7, 0x28, 0x00, 0x40, 0x40, 0x20, 0x10, 0x12, + 0x58, 0x20, 0xd0, 0x00, 0xb2, 0x20, 0x00, 0x21, 0xb2, 0x22, 0x00, 0x30, + 0x88, 0x30, 0x00, 0x1c, 0xc0, 0xe5, 0x00, 0x00, 0x00, 0x63, 0xa7, 0x29, + 0x00, 0x41, 0xc0, 0xe5, 0x00, 0x00, 0x00, 0xbf, 0xa5, 0x2e, 0x00, 0x10, + 0xa7, 0x19, 0x63, 0x00, 0x92, 0x00, 0x20, 0x00, 0xa7, 0x2b, 0x10, 0x00, + 0xa7, 0x17, 0xff, 0xfc, 0xa5, 0x1e, 0x00, 0x10, 0xa7, 0x29, 0x63, 0x00, + 0xe3, 0x30, 0x10, 0x00, 0x00, 0x90, 0xa7, 0x3a, 0x00, 0x01, 0x42, 0x30, + 0x10, 0x00, 0xa7, 0x1b, 0x10, 0x00, 0xa7, 0x27, 0xff, 0xf7, 0xa7, 0x29, + 0x00, 0x42, 0xc0, 0xe5, 0x00, 0x00, 0x00, 0xa1, 0xa7, 0xf4, 0xff, 0xec, + 0xc0, 0xf0, 0x00, 0x00, 0x56, 0x30, 0xc0, 0x20, 0x00, 0x00, 0x0e, 0x7d, + 0xe3, 0x20, 0x20, 0x00, 0x00, 0x04, 0xc0, 0x30, 0x00, 0x00, 0x9e, 0x77, + 0xb9, 0x0b, 0x00, 0x32, 0xb9, 0x02, 0x00, 0x33, 0xa7, 0x84, 0x00, 0x19, + 0xa7, 0x3b, 0xff, 0xff, 0xeb, 0x43, 0x00, 0x08, 0x00, 0x0c, 0xb9, 0x02, + 0x00, 0x44, 0xb9, 0x04, 0x00, 0x12, 0xa7, 0x84, 0x00, 0x09, 0xd7, 0xff, + 0x10, 0x00, 0x10, 0x00, 0x41, 0x10, 0x11, 0x00, 0xa7, 0x47, 0xff, 0xfb, + 0xc0, 0x20, 0x00, 0x00, 0x00, 0x0d, 0x44, 0x30, 0x20, 0x00, 0xc0, 0x20, + 0x00, 0x00, 0x00, 0x57, 0xd2, 0x0f, 0x01, 0xd0, 0x20, 0x00, 0xa7, 0xf4, + 0xff, 0x89, 0xd7, 0x00, 0x10, 0x00, 0x10, 0x00, 0xc0, 0x10, 0x00, 0x00, + 0x00, 0x4c, 0xb2, 0xb2, 0x10, 0x00, 0xa7, 0xf4, 0x00, 0x00, 0xeb, 0x00, + 0xf0, 0x00, 0x00, 0x25, 0x96, 0x02, 0xf0, 0x06, 0xeb, 0x00, 0xf0, 0x00, + 0x00, 0x2f, 0xc0, 0x10, 0x00, 0x00, 0x00, 0x2a, 0xe3, 0x10, 0x01, 0xb8, + 0x00, 0x24, 0xc0, 0x10, 0x00, 0x00, 0x00, 0x47, 0xd2, 0x07, 0x01, 0xb0, + 0x10, 0x00, 0xc0, 0x10, 0x00, 0x00, 0x00, 0x39, 0xb2, 0xb2, 0x10, 0x00, + 0xeb, 0x66, 0xf0, 0x00, 0x00, 0x25, 0x96, 0xff, 0xf0, 0x04, 0xeb, 0x66, + 0xf0, 0x00, 0x00, 0x2f, 0xc0, 0x10, 0x00, 0x00, 0x00, 0x1a, 0xe3, 0x10, + 0x01, 0xf8, 0x00, 0x24, 0xc0, 0x10, 0x00, 0x00, 0x00, 0x32, 0xd2, 0x07, + 0x01, 0xf0, 0x10, 0x00, 0xc0, 0x10, 0x00, 0x00, 0x00, 0x20, 0xb2, 0xb2, + 0x10, 0x00, 0xeb, 0x00, 0xf0, 0x00, 0x00, 0x25, 0x94, 0xfd, 0xf0, 0x06, + 0xeb, 0x00, 0xf0, 0x00, 0x00, 0x2f, 0x07, 0xfe, 0xeb, 0x66, 0xf0, 0x00, + 0x00, 0x25, 0x94, 0x00, 0xf0, 0x04, 0xeb, 0x66, 0xf0, 0x00, 0x00, 0x2f, + 0x07, 0xfe, 0x07, 0x07, 0x00, 0x02, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x02, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x03, 0x02, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, 0xeb, 0xbf, 0xf0, 0x58, - 0x00, 0x24, 0xc0, 0x10, 0x00, 0x00, 0x4e, 0x0d, 0xa7, 0xfb, 0xff, 0x60, - 0xb2, 0x20, 0x00, 0x21, 0xb2, 0x22, 0x00, 0xb0, 0x88, 0xb0, 0x00, 0x1c, - 0xc0, 0xe5, 0xff, 0xff, 0xff, 0x91, 0xa7, 0xbe, 0x00, 0x03, 0xa7, 0x84, - 0x00, 0x13, 0xa7, 0xbe, 0x00, 0x02, 0xa7, 0x28, 0x00, 0x00, 0xa7, 0x74, - 0x00, 0x04, 0xa7, 0x28, 0xff, 0xfe, 0xe3, 0x40, 0xf1, 0x10, 0x00, 0x04, - 0xb9, 0x14, 0x00, 0x22, 0xeb, 0xbf, 0xf0, 0xf8, 0x00, 0x04, 0x07, 0xf4, - 0xa7, 0x28, 0xff, 0xff, 0xa7, 0xf4, 0xff, 0xf5, 0x07, 0x07, 0x07, 0x07, - 0xeb, 0xbf, 0xf0, 0x58, 0x00, 0x24, 0xc0, 0xd0, 0x00, 0x00, 0x01, 0x25, - 0xa7, 0xfb, 0xff, 0x60, 0xa7, 0xb9, 0x00, 0x00, 0xa7, 0x19, 0x00, 0x00, - 0xc0, 0x40, 0x00, 0x00, 0x4d, 0xd8, 0xa7, 0x3b, 0x00, 0x01, 0xa7, 0x37, - 0x00, 0x23, 0xc0, 0x20, 0x00, 0x00, 0x4d, 0xd1, 0x18, 0x31, 0xa7, 0x1a, - 0x00, 0x06, 0x40, 0x10, 0x20, 0x08, 0xa7, 0x3a, 0x00, 0x0e, 0xa7, 0x18, - 0x1a, 0x00, 0x40, 0x30, 0x20, 0x00, 0x92, 0x00, 0x20, 0x02, 0x40, 0x10, - 0x20, 0x0a, 0xe3, 0x20, 0xd0, 0x00, 0x00, 0x04, 0xc0, 0xe5, 0xff, 0xff, - 0xff, 0xac, 0xe3, 0x40, 0xf1, 0x10, 0x00, 0x04, 0xb9, 0x04, 0x00, 0x2b, - 0xeb, 0xbf, 0xf0, 0xf8, 0x00, 0x04, 0x07, 0xf4, 0xb9, 0x04, 0x00, 0x51, - 0xa7, 0x5b, 0x00, 0x01, 0xa7, 0x09, 0x0f, 0xf7, 0xb9, 0x21, 0x00, 0x50, - 0xa7, 0x24, 0xff, 0xd7, 0x41, 0xeb, 0x20, 0x00, 0x95, 0x0a, 0xe0, 0x00, - 0xa7, 0x74, 0x00, 0x08, 0x41, 0x11, 0x40, 0x0e, 0x92, 0x0d, 0x10, 0x00, - 0xb9, 0x04, 0x00, 0x15, 0x43, 0x5b, 0x20, 0x00, 0x42, 0x51, 0x40, 0x0e, - 0xa7, 0xbb, 0x00, 0x01, 0x41, 0x10, 0x10, 0x01, 0xa7, 0xf4, 0xff, 0xbf, - 0xc0, 0x50, 0x00, 0x00, 0x00, 0xd8, 0xc0, 0x10, 0x00, 0x00, 0x4d, 0x8d, - 0xa7, 0x48, 0x00, 0x1c, 0x40, 0x40, 0x10, 0x00, 0x50, 0x20, 0x10, 0x0c, - 0xa7, 0x48, 0x00, 0x04, 0xe3, 0x20, 0x50, 0x00, 0x00, 0x04, 0x40, 0x40, - 0x10, 0x0a, 0x50, 0x30, 0x10, 0x10, 0xc0, 0xf4, 0xff, 0xff, 0xff, 0x6b, - 0xa7, 0x39, 0x00, 0x40, 0xa7, 0x29, 0x00, 0x00, 0xc0, 0xf4, 0xff, 0xff, - 0xff, 0xe4, 0x07, 0x07, 0xb9, 0x04, 0x00, 0x13, 0xa7, 0x2a, 0xff, 0xff, - 0xb9, 0x04, 0x00, 0x34, 0xa7, 0x48, 0x00, 0x01, 0x15, 0x24, 0xa7, 0x24, - 0x00, 0x07, 0xb9, 0x04, 0x00, 0x21, 0xc0, 0xf4, 0xff, 0xff, 0xff, 0x7f, - 0xa7, 0x29, 0xff, 0xff, 0x07, 0xfe, 0x07, 0x07, 0xa7, 0x39, 0x00, 0x00, - 0x41, 0x13, 0x20, 0x00, 0x95, 0x00, 0x10, 0x00, 0xa7, 0x74, 0x00, 0x05, - 0xc0, 0xf4, 0xff, 0xff, 0xff, 0x70, 0xa7, 0x3b, 0x00, 0x01, 0xa7, 0xf4, - 0xff, 0xf5, 0x07, 0x07, 0xeb, 0xbf, 0xf0, 0x58, 0x00, 0x24, 0xc0, 0xd0, - 0x00, 0x00, 0x00, 0x95, 0xa7, 0xfb, 0xff, 0x60, 0xb9, 0x04, 0x00, 0xb2, - 0xa7, 0x19, 0x00, 0x20, 0xc0, 0x20, 0x00, 0x00, 0x4d, 0x40, 0x92, 0x00, - 0x20, 0x00, 0xa7, 0x2b, 0x00, 0x01, 0xa7, 0x17, 0xff, 0xfc, 0xc0, 0x10, - 0x00, 0x00, 0x4d, 0x37, 0xa7, 0x28, 0x10, 0x00, 0x40, 0x20, 0x10, 0x00, - 0xe3, 0x20, 0xd0, 0x00, 0x00, 0x04, 0xc0, 0xe5, 0xff, 0xff, 0xff, 0x1d, - 0x12, 0x22, 0xa7, 0x74, 0x00, 0x19, 0xa7, 0x19, 0x00, 0x00, 0xc0, 0x40, - 0x00, 0x00, 0x00, 0x79, 0xa7, 0x39, 0x00, 0x08, 0xc0, 0x20, 0x00, 0x00, - 0x4d, 0x2c, 0x41, 0x21, 0x20, 0x00, 0xe3, 0x20, 0x20, 0x00, 0x00, 0x90, - 0x43, 0x22, 0x40, 0x00, 0x42, 0x21, 0xb0, 0x00, 0xa7, 0x1b, 0x00, 0x01, - 0xa7, 0x37, 0xff, 0xf2, 0xe3, 0x40, 0xf1, 0x10, 0x00, 0x04, 0xeb, 0xbf, - 0xf0, 0xf8, 0x00, 0x04, 0x07, 0xf4, 0x07, 0x07, 0xeb, 0xaf, 0xf0, 0x50, - 0x00, 0x24, 0xc0, 0xd0, 0x00, 0x00, 0x00, 0x55, 0xa7, 0xfb, 0xff, 0x60, - 0xa7, 0x19, 0x0f, 0xf8, 0xb9, 0x21, 0x00, 0x31, 0xb9, 0x04, 0x00, 0xa2, - 0xa7, 0xc4, 0x00, 0x2a, 0xa7, 0xb9, 0x0f, 0xf8, 0xc0, 0x10, 0x00, 0x00, - 0x4c, 0xf6, 0xa7, 0x28, 0x10, 0x00, 0x40, 0x20, 0x10, 0x00, 0x92, 0x00, - 0x10, 0x02, 0xe3, 0x20, 0xd0, 0x00, 0x00, 0x04, 0xc0, 0xe5, 0xff, 0xff, - 0xfe, 0xda, 0xa7, 0xbb, 0x00, 0x01, 0xa7, 0x19, 0x00, 0x00, 0xa7, 0xb7, - 0x00, 0x17, 0xc0, 0x10, 0x00, 0x00, 0x4c, 0xe1, 0xe3, 0x40, 0xf1, 0x10, - 0x00, 0x04, 0xe3, 0x20, 0x10, 0x08, 0x00, 0x91, 0xa7, 0x2a, 0xff, 0xf9, - 0xb9, 0x14, 0x00, 0x22, 0xeb, 0xaf, 0xf0, 0xf0, 0x00, 0x04, 0x07, 0xf4, - 0xb9, 0x04, 0x00, 0xb3, 0xa7, 0xf4, 0xff, 0xd8, 0xc0, 0x20, 0x00, 0x00, - 0x4c, 0xcc, 0x41, 0x31, 0xa0, 0x00, 0x41, 0x21, 0x20, 0x00, 0xa7, 0x1b, - 0x00, 0x01, 0xd2, 0x00, 0x30, 0x00, 0x20, 0x0f, 0xa7, 0xf4, 0xff, 0xdd, - 0x07, 0x07, 0x07, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0x00, 0x05, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, - 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, - 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, - 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, - 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, - 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, - 0x2e, 0x2e, 0x2e, 0x2e, 0x20, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, - 0x2e, 0x2e, 0x2e, 0x2e, 0x3c, 0x28, 0x2b, 0x7c, 0x26, 0x2e, 0x2e, 0x2e, - 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x21, 0x24, 0x2a, 0x29, 0x3b, 0x2e, - 0x2d, 0x2f, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2c, - 0x25, 0x5f, 0x3e, 0x3f, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, - 0x2e, 0x60, 0x3a, 0x23, 0x40, 0x27, 0x3d, 0x22, 0x2e, 0x61, 0x62, 0x63, - 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, - 0x2e, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x2e, 0x2e, - 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, - 0x79, 0x7a, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, - 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, - 0x2e, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x2e, 0x2e, - 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, - 0x51, 0x52, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x53, 0x54, - 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, - 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x2e, 0x2e, - 0x2e, 0x2e, 0x2e, 0x2e, 0x41, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x6f, 0xff, 0xfe, 0xf5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xd8, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x02, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xf8, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x30, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x6f, 0xff, 0xff, 0xfb, - 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x6f, 0xff, 0xff, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x80, 0x00, 0x00, 0x00, 0x07, 0xfe, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0xc0, 0x50, 0x00, 0x00, 0x00, 0x20, 0xc0, 0x10, 0x00, 0x00, 0x95, 0xe1, + 0x42, 0x20, 0x10, 0x0e, 0xa7, 0x28, 0x00, 0x0f, 0x40, 0x20, 0x10, 0x00, + 0x58, 0x20, 0x50, 0x04, 0x50, 0x20, 0x10, 0x08, 0x92, 0x00, 0x10, 0x02, + 0x58, 0x20, 0x50, 0x00, 0xb2, 0x20, 0x00, 0x21, 0xb2, 0x22, 0x00, 0x30, + 0x88, 0x30, 0x00, 0x1c, 0xc0, 0xf4, 0xff, 0xff, 0xff, 0x85, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x00, 0x76, 0x00, 0x05, 0x00, 0x07, 0x1a, 0x00, + 0x00, 0x78, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -XXX,XX +XXX,XX @@ unsigned char s390x_elf[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x17, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x43, 0x43, 0x3a, - 0x20, 0x28, 0x55, 0x62, 0x75, 0x6e, 0x74, 0x75, 0x20, 0x31, 0x31, 0x2e, - 0x34, 0x2e, 0x30, 0x2d, 0x31, 0x75, 0x62, 0x75, 0x6e, 0x74, 0x75, 0x31, - 0x7e, 0x32, 0x32, 0x2e, 0x30, 0x34, 0x29, 0x20, 0x31, 0x31, 0x2e, 0x34, - 0x2e, 0x30, 0x00, 0x00, 0x2e, 0x73, 0x68, 0x73, 0x74, 0x72, 0x74, 0x61, - 0x62, 0x00, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x00, 0x2e, 0x67, - 0x6e, 0x75, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x00, 0x2e, 0x64, 0x79, 0x6e, - 0x73, 0x79, 0x6d, 0x00, 0x2e, 0x64, 0x79, 0x6e, 0x73, 0x74, 0x72, 0x00, - 0x2e, 0x72, 0x65, 0x6c, 0x61, 0x2e, 0x64, 0x79, 0x6e, 0x00, 0x2e, 0x74, - 0x65, 0x78, 0x74, 0x00, 0x2e, 0x72, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x00, - 0x2e, 0x64, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x00, 0x2e, 0x67, 0x6f, - 0x74, 0x00, 0x2e, 0x62, 0x73, 0x73, 0x00, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, - 0x65, 0x6e, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xc8, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xc8, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xd8, + 0x00, 0x00, 0x00, 0x00, 0x6f, 0xff, 0xfe, 0xf5, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x6f, 0xff, 0xff, 0xfb, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6f, 0xff, 0xff, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x6f, 0xff, 0xff, 0xf6, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0xd8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, + 0x47, 0x43, 0x43, 0x3a, 0x20, 0x28, 0x53, 0x55, 0x53, 0x45, 0x20, 0x4c, + 0x69, 0x6e, 0x75, 0x78, 0x29, 0x20, 0x31, 0x32, 0x2e, 0x33, 0x2e, 0x30, + 0x00, 0x00, 0x2e, 0x73, 0x68, 0x73, 0x74, 0x72, 0x74, 0x61, 0x62, 0x00, + 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x00, 0x2e, 0x67, 0x6e, 0x75, + 0x2e, 0x68, 0x61, 0x73, 0x68, 0x00, 0x2e, 0x64, 0x79, 0x6e, 0x73, 0x79, + 0x6d, 0x00, 0x2e, 0x64, 0x79, 0x6e, 0x73, 0x74, 0x72, 0x00, 0x2e, 0x72, + 0x65, 0x6c, 0x61, 0x2e, 0x64, 0x79, 0x6e, 0x00, 0x2e, 0x74, 0x65, 0x78, + 0x74, 0x00, 0x2e, 0x72, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x00, 0x2e, 0x65, + 0x68, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x00, 0x2e, 0x64, 0x79, 0x6e, + 0x61, 0x6d, 0x69, 0x63, 0x00, 0x2e, 0x67, 0x6f, 0x74, 0x00, 0x2e, 0x64, + 0x61, 0x74, 0x61, 0x00, 0x2e, 0x62, 0x73, 0x73, 0x00, 0x2e, 0x63, 0x6f, + 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x01, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xd8, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x03, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1d, - 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xf8, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x01, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, - 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, - 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x28, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x28, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x01, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xc8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, + 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xd8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, + 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x13, 0x6f, 0xff, 0xff, 0xf6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x02, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x30, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x02, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x05, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x25, + 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x58, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x58, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x70, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x37, - 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x48, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x02, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x78, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x88, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x88, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x01, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x88, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x88, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, 0x06, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4f, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x17, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xb8, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x20, 0x00, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x1e, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0xd8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x4e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0xd8, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x08, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xe8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0f, 0xe8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x5d, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0xf0, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0xf0, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x10, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x09, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x21, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x71, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 }; diff --git a/tests/qtest/migration/s390x/a-b-bios.c b/tests/qtest/migration/s390x/a-b-bios.c index XXXXXXX..XXXXXXX 100644 --- a/tests/qtest/migration/s390x/a-b-bios.c +++ b/tests/qtest/migration/s390x/a-b-bios.c @@ -XXX,XX +XXX,XX @@ * option) any later version. */ -#define LOADPARM_LEN 8 /* Needed for sclp.h */ +/* for sclp.h */ +#define LOADPARM_LEN 8 +typedef unsigned int uint32_t; +typedef unsigned short uint16_t; +typedef unsigned char uint8_t; -#include <libc.h> -#include <s390-ccw.h> #include <sclp.h> -char stack[0x8000] __attribute__((aligned(4096))); - #define START_ADDRESS (1024 * 1024) #define END_ADDRESS (100 * 1024 * 1024) +/* at pc-bios/s390-ccw/start.S */ + +void __attribute__((weak)) consume_sclp_int(void) +{ +} + +static char _sccb[4096] __attribute__((__aligned__(4096))); +char stack[0x8000] __attribute__((aligned(4096))); + +static void sclp_service_call(unsigned int command) +{ + int cc; + + asm volatile( + " .insn rre,0xb2200000,%1,%2\n" /* servc %1,%2 */ + " ipm %0\n" + " srl %0,28" + : "=&d" (cc) : "d" (command), "a" (__pa(_sccb)) + : "cc", "memory"); + consume_sclp_int(); +} + +static void sclp_setup(void) +{ + WriteEventMask *sccb = (void *)_sccb; + + sccb->h.length = sizeof(WriteEventMask); + + sccb->mask_length = sizeof(unsigned int); + sccb->cp_receive_mask = 0; + sccb->cp_send_mask = SCLP_EVENT_MASK_MSG_ASCII; + + sclp_service_call(SCLP_CMD_WRITE_EVENT_MASK); +} + +static void putc(const char c) +{ + WriteEventData *sccb = (void *)_sccb; + int len = 1; + + sccb->data[0] = c; + + sccb->h.length = sizeof(WriteEventData) + len; + sccb->h.function_code = SCLP_FC_NORMAL_WRITE; + + sccb->ebh.length = sizeof(EventBufferHeader) + len; + sccb->ebh.type = SCLP_EVENT_ASCII_CONSOLE_DATA; + sccb->ebh.flags = 0; + + sclp_service_call(SCLP_CMD_WRITE_EVENT_DATA); +} + void main(void) { unsigned long addr; sclp_setup(); - sclp_print("A"); + putc('A'); /* * Make sure all of the pages have consistent contents before incrementing @@ -XXX,XX +XXX,XX @@ void main(void) for (addr = START_ADDRESS; addr < END_ADDRESS; addr += 4096) { *(volatile char *)addr += 1; /* Change pages */ } - sclp_print("B"); + putc('B'); } } diff --git a/tests/qtest/migration/Makefile b/tests/qtest/migration/Makefile index XXXXXXX..XXXXXXX 100644 --- a/tests/qtest/migration/Makefile +++ b/tests/qtest/migration/Makefile @@ -XXX,XX +XXX,XX @@ TARGET_LIST = i386 aarch64 s390x ppc64 -SRC_PATH = ../.. +SRC_PATH = ../../../.. .PHONY: help $(TARGET_LIST) help: @@ -XXX,XX +XXX,XX @@ help: @echo " Possible targets are: $(TARGET_LIST)" override define __note -/* This file is automatically generated from the assembly file in - * tests/migration/$@. Edit that file and then run "make all" - * inside tests/migration to update, and then remember to send both +/* + * This file is automatically generated from the assembly file in + * tests/qtest/migration/$@. Edit that file and then run "make" + * inside tests/qtest/migration to update, and then remember to send both * the header and the assembler differences in your patch submission. */ endef export __note $(TARGET_LIST): - $(MAKE) CROSS_PREFIX=$(CROSS_PREFIX) -C $@ + $(MAKE) SRC_PATH=$(SRC_PATH) CROSS_PREFIX=$(CROSS_PREFIX) -C $@ clean: for target in $(TARGET_LIST); do \ diff --git a/tests/qtest/migration/aarch64/Makefile b/tests/qtest/migration/aarch64/Makefile index XXXXXXX..XXXXXXX 100644 --- a/tests/qtest/migration/aarch64/Makefile +++ b/tests/qtest/migration/aarch64/Makefile @@ -XXX,XX +XXX,XX @@ all: a-b-kernel.h a-b-kernel.h: aarch64.kernel echo "$$__note" > $@ - xxd -i $< | sed -e 's/.*int.*//' >> $@ + xxd -i $< | sed -e '/.*int.*/d' >> $@ aarch64.kernel: aarch64.elf $(CROSS_PREFIX)objcopy -O binary $< $@ @@ -XXX,XX +XXX,XX @@ aarch64.elf: a-b-kernel.S $(CROSS_PREFIX)gcc -o $@ -nostdlib -Wl,--build-id=none $< clean: - $(RM) *.kernel *.elf + $(RM) *.kernel *.elf *.h diff --git a/tests/qtest/migration/aarch64/a-b-kernel.S b/tests/qtest/migration/aarch64/a-b-kernel.S index XXXXXXX..XXXXXXX 100644 --- a/tests/qtest/migration/aarch64/a-b-kernel.S +++ b/tests/qtest/migration/aarch64/a-b-kernel.S @@ -XXX,XX +XXX,XX @@ # pc-relative address. Also the branch instructions should use relative # addresses only. -#include "../migration-test.h" +#include "../bootfile.h" .section .text diff --git a/tests/qtest/migration/i386/Makefile b/tests/qtest/migration/i386/Makefile index XXXXXXX..XXXXXXX 100644 --- a/tests/qtest/migration/i386/Makefile +++ b/tests/qtest/migration/i386/Makefile @@ -XXX,XX +XXX,XX @@ all: a-b-bootblock.h a-b-bootblock.h: x86.bootsect x86.o echo "$$__note" > header.tmp - xxd -i $< | sed -e 's/.*int.*//' >> header.tmp + xxd -i $< | sed -e '/.*int.*/d' >> header.tmp nm x86.o | awk '{print "#define SYM_"$$3" 0x"$$1}' >> header.tmp mv header.tmp $@ @@ -XXX,XX +XXX,XX @@ x86.o: a-b-bootblock.S $(CROSS_PREFIX)gcc -I.. -m32 -march=i486 -c $< -o $@ clean: - @rm -rf *.boot *.o *.bootsect + @rm -rf *.boot *.o *.bootsect *.h diff --git a/tests/qtest/migration/i386/a-b-bootblock.S b/tests/qtest/migration/i386/a-b-bootblock.S index XXXXXXX..XXXXXXX 100644 --- a/tests/qtest/migration/i386/a-b-bootblock.S +++ b/tests/qtest/migration/i386/a-b-bootblock.S @@ -XXX,XX +XXX,XX @@ # # Author: dgilbert@redhat.com -#include "migration-test.h" +#include "../bootfile.h" #define ACPI_ENABLE 0xf1 #define ACPI_PORT_SMI_CMD 0xb2 diff --git a/tests/qtest/migration/ppc64/Makefile b/tests/qtest/migration/ppc64/Makefile index XXXXXXX..XXXXXXX 100644 --- a/tests/qtest/migration/ppc64/Makefile +++ b/tests/qtest/migration/ppc64/Makefile @@ -XXX,XX +XXX,XX @@ all: a-b-kernel.h a-b-kernel.h: ppc64.kernel echo "$$__note" > $@ - xxd -i $< | sed -e 's/.*int.*//' >> $@ + xxd -i $< | sed -e '/.*int.*/d' >> $@ ppc64.kernel: ppc64.elf $(CROSS_PREFIX)objcopy -O binary -S $< $@ ppc64.elf: a-b-kernel.S - $(CROSS_PREFIX)gcc -static -o $@ -nostdlib -Wl,--build-id=none $< + $(CROSS_PREFIX)gcc -mbig-endian -static -o $@ -nostdlib -Wl,--build-id=none $< clean: - $(RM) *.kernel *.elf + $(RM) *.kernel *.elf *.h diff --git a/tests/qtest/migration/ppc64/a-b-kernel.S b/tests/qtest/migration/ppc64/a-b-kernel.S index XXXXXXX..XXXXXXX 100644 --- a/tests/qtest/migration/ppc64/a-b-kernel.S +++ b/tests/qtest/migration/ppc64/a-b-kernel.S @@ -XXX,XX +XXX,XX @@ # This work is licensed under the terms of the GNU GPL, version 2 or later. # See the COPYING file in the top-level directory. -#include "../migration-test.h" +#include "../bootfile.h" .section .text diff --git a/tests/qtest/migration/s390x/Makefile b/tests/qtest/migration/s390x/Makefile index XXXXXXX..XXXXXXX 100644 --- a/tests/qtest/migration/s390x/Makefile +++ b/tests/qtest/migration/s390x/Makefile @@ -XXX,XX +XXX,XX @@ .PHONY: all clean all: a-b-bios.h -fwdir=../../../pc-bios/s390-ccw +fwdir=$(SRC_PATH)/pc-bios/s390-ccw CFLAGS+=-ffreestanding -fno-delete-null-pointer-checks -fPIE -Os \ -msoft-float -march=z900 -fno-asynchronous-unwind-tables \ @@ -XXX,XX +XXX,XX @@ CFLAGS+=-ffreestanding -fno-delete-null-pointer-checks -fPIE -Os \ a-b-bios.h: s390x.elf echo "$$__note" > header.tmp - xxd -i $< | sed -e 's/.*int.*//' >> header.tmp + xxd -i $< | sed -e '/.*int.*/d' >> header.tmp mv header.tmp $@ # We use common-page-size=16 to avoid big padding in the ELF file s390x.elf: a-b-bios.c $(CROSS_PREFIX)gcc $(CFLAGS) -I$(fwdir) $(fwdir)/start.S \ - $(fwdir)/sclp.c -Wl,-zcommon-page-size=16 -o $@ $< + -Wl,-zcommon-page-size=16 -o $@ $< $(CROSS_PREFIX)strip $@ clean: - @rm -rf *.elf *.o + @rm -rf *.elf *.o *.h -- 2.53.0