Migration pull request for softfreeze
v2: - Patch "migration: Move cpu-throttle.c from system to migration", fix build on MacOS, and subject spelling NOTE: checkpatch.pl could report a false positive on this branch: WARNING: added, moved or deleted file(s), does MAINTAINERS need updating? #21: {include/sysemu => migration}/cpu-throttle.h | 0 That's covered by "F: migration/" entry. Changelog: - Peter's cleanup patch on migrate_fd_cleanup() - Peter's cleanup patch to introduce thread name macros - Hanna's error path fix for vmstate subsection save()s - Hyman's auto converge enhancement on background dirty sync - Peter's additional tracepoints for save state entries - Thomas's build fix for OpenBSD in dirtyrate.c - Peter's deprecation of query-migrationthreads command - Peter's cleanup/fixes from the "export misc.h" series - Maciej's two small patches from multifd+vfio series -----BEGIN PGP SIGNATURE----- iIgEABYKADAWIQS5GE3CDMRX2s990ak7X8zN86vXBgUCZyTbVRIccGV0ZXJ4QHJl ZGhhdC5jb20ACgkQO1/MzfOr1wan3wD+L4TVNDc34Hy4mvWu7u1lCOePX0GBdUEc oEeBGblwbrcBAIR8d+5z9O5YcWH1coozG1aUC4qCtSHHk5TGbJk4/UUD =XB5Q -----END PGP SIGNATURE----- Merge tag 'migration-20241030-pull-request' of https://gitlab.com/peterx/qemu into staging Migration pull request for softfreeze v2: - Patch "migration: Move cpu-throttle.c from system to migration", fix build on MacOS, and subject spelling NOTE: checkpatch.pl could report a false positive on this branch: WARNING: added, moved or deleted file(s), does MAINTAINERS need updating? #21: {include/sysemu => migration}/cpu-throttle.h | 0 That's covered by "F: migration/" entry. Changelog: - Peter's cleanup patch on migrate_fd_cleanup() - Peter's cleanup patch to introduce thread name macros - Hanna's error path fix for vmstate subsection save()s - Hyman's auto converge enhancement on background dirty sync - Peter's additional tracepoints for save state entries - Thomas's build fix for OpenBSD in dirtyrate.c - Peter's deprecation of query-migrationthreads command - Peter's cleanup/fixes from the "export misc.h" series - Maciej's two small patches from multifd+vfio series # -----BEGIN PGP SIGNATURE----- # # iIgEABYKADAWIQS5GE3CDMRX2s990ak7X8zN86vXBgUCZyTbVRIccGV0ZXJ4QHJl # ZGhhdC5jb20ACgkQO1/MzfOr1wan3wD+L4TVNDc34Hy4mvWu7u1lCOePX0GBdUEc # oEeBGblwbrcBAIR8d+5z9O5YcWH1coozG1aUC4qCtSHHk5TGbJk4/UUD # =XB5Q # -----END PGP SIGNATURE----- # gpg: Signature made Fri 01 Nov 2024 13:44:53 GMT # gpg: using EDDSA key B9184DC20CC457DACF7DD1A93B5FCCCDF3ABD706 # gpg: issuer "peterx@redhat.com" # gpg: Good signature from "Peter Xu <xzpeter@gmail.com>" [marginal] # gpg: aka "Peter Xu <peterx@redhat.com>" [marginal] # gpg: WARNING: This key is not certified with sufficiently trusted signatures! # gpg: It is not certain that the signature belongs to the owner. # Primary key fingerprint: B918 4DC2 0CC4 57DA CF7D D1A9 3B5F CCCD F3AB D706 * tag 'migration-20241030-pull-request' of https://gitlab.com/peterx/qemu: migration/multifd: Zero p->flags before starting filling a packet migration/ram: Add load start trace event migration: Drop migration_is_idle() migration: Drop migration_is_setup_or_active() migration: Unexport ram_mig_init() migration: Unexport dirty_bitmap_mig_init() migration: Take migration object refcount earlier for threads migration: Deprecate query-migrationthreads command migration/dirtyrate: Silence warning about strcpy() on OpenBSD tests/migration: Add case for periodic ramblock dirty sync migration: Support periodic RAMBlock dirty bitmap sync migration: Remove "rs" parameter in migration_bitmap_sync_precopy migration: Move cpu-throttle.c from system to migration migration: Stop CPU throttling conditionally accel/tcg/icount-common: Remove the reference to the unused header file migration: Ensure vmstate_save() sets errp migration: Put thread names together with macros migration: Cleanup migrate_fd_cleanup() on accessing to_dst_file Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
cbad455118
@ -36,7 +36,6 @@
|
||||
#include "sysemu/runstate.h"
|
||||
#include "hw/core/cpu.h"
|
||||
#include "sysemu/cpu-timers.h"
|
||||
#include "sysemu/cpu-throttle.h"
|
||||
#include "sysemu/cpu-timers-internal.h"
|
||||
|
||||
/*
|
||||
|
@ -147,6 +147,14 @@ options are removed in favor of using explicit ``blockdev-create`` and
|
||||
``blockdev-add`` calls. See :doc:`/interop/live-block-operations` for
|
||||
details.
|
||||
|
||||
``query-migrationthreads`` (since 9.2)
|
||||
''''''''''''''''''''''''''''''''''''''
|
||||
|
||||
To be removed with no replacement, as it reports only a limited set of
|
||||
threads (for example, it only reports source side of multifd threads,
|
||||
without reporting any destination threads, or non-multifd source threads).
|
||||
For debugging purpose, please use ``-name $VM,debug-threads=on`` instead.
|
||||
|
||||
Incorrectly typed ``device_add`` arguments (since 6.2)
|
||||
''''''''''''''''''''''''''''''''''''''''''''''''''''''
|
||||
|
||||
|
@ -149,7 +149,7 @@ bool vfio_viommu_preset(VFIODevice *vbasedev)
|
||||
|
||||
static void vfio_set_migration_error(int ret)
|
||||
{
|
||||
if (migration_is_setup_or_active()) {
|
||||
if (migration_is_running()) {
|
||||
migration_file_set_error(ret, NULL);
|
||||
}
|
||||
}
|
||||
|
@ -188,7 +188,7 @@ static bool virtio_mem_is_busy(void)
|
||||
* after plugging them) until we're running on the destination (as we didn't
|
||||
* migrate these blocks when they were unplugged).
|
||||
*/
|
||||
return migration_in_incoming_postcopy() || !migration_is_idle();
|
||||
return migration_in_incoming_postcopy() || migration_is_running();
|
||||
}
|
||||
|
||||
typedef int (*virtio_mem_range_cb)(VirtIOMEM *vmem, void *arg,
|
||||
|
@ -39,7 +39,6 @@ void precopy_add_notifier(NotifierWithReturn *n);
|
||||
void precopy_remove_notifier(NotifierWithReturn *n);
|
||||
int precopy_notify(PrecopyNotifyReason reason, Error **errp);
|
||||
|
||||
void ram_mig_init(void);
|
||||
void qemu_guest_free_page_hint(void *addr, size_t len);
|
||||
bool migrate_ram_is_ignored(RAMBlock *block);
|
||||
|
||||
@ -53,11 +52,11 @@ void dump_vmstate_json_to_file(FILE *out_fp);
|
||||
/* migration/migration.c */
|
||||
void migration_object_init(void);
|
||||
void migration_shutdown(void);
|
||||
bool migration_is_idle(void);
|
||||
|
||||
bool migration_is_active(void);
|
||||
bool migration_is_device(void);
|
||||
bool migration_is_running(void);
|
||||
bool migration_thread_is_self(void);
|
||||
bool migration_is_setup_or_active(void);
|
||||
|
||||
typedef enum MigrationEventType {
|
||||
MIG_EVENT_PRECOPY_SETUP,
|
||||
@ -96,7 +95,6 @@ void migration_add_notifier_mode(NotifierWithReturn *notify,
|
||||
MigrationNotifyFunc func, MigMode mode);
|
||||
|
||||
void migration_remove_notifier(NotifierWithReturn *notify);
|
||||
bool migration_is_running(void);
|
||||
void migration_file_set_error(int ret, Error *err);
|
||||
|
||||
/* True if incoming migration entered POSTCOPY_INCOMING_DISCARD */
|
||||
@ -108,7 +106,4 @@ bool migration_incoming_postcopy_advised(void);
|
||||
/* True if background snapshot is active */
|
||||
bool migration_in_bg_snapshot(void);
|
||||
|
||||
/* migration/block-dirty-bitmap.c */
|
||||
void dirty_bitmap_mig_init(void);
|
||||
|
||||
#endif
|
||||
|
@ -65,4 +65,18 @@ bool cpu_throttle_active(void);
|
||||
*/
|
||||
int cpu_throttle_get_percentage(void);
|
||||
|
||||
/**
|
||||
* cpu_throttle_dirty_sync_timer_tick:
|
||||
*
|
||||
* Dirty sync timer hook.
|
||||
*/
|
||||
void cpu_throttle_dirty_sync_timer_tick(void *opaque);
|
||||
|
||||
/**
|
||||
* cpu_throttle_dirty_sync_timer:
|
||||
*
|
||||
* Start or stop the dirty sync timer.
|
||||
*/
|
||||
void cpu_throttle_dirty_sync_timer(bool enable);
|
||||
|
||||
#endif /* SYSEMU_CPU_THROTTLE_H */
|
||||
|
@ -935,7 +935,8 @@ void coroutine_fn colo_incoming_co(void)
|
||||
assert(bql_locked());
|
||||
assert(migration_incoming_colo_enabled());
|
||||
|
||||
qemu_thread_create(&th, "mig/dst/colo", colo_process_incoming_thread,
|
||||
qemu_thread_create(&th, MIGRATION_THREAD_DST_COLO,
|
||||
colo_process_incoming_thread,
|
||||
mis, QEMU_THREAD_JOINABLE);
|
||||
|
||||
mis->colo_incoming_co = qemu_coroutine_self();
|
||||
|
@ -28,16 +28,23 @@
|
||||
#include "qemu/main-loop.h"
|
||||
#include "sysemu/cpus.h"
|
||||
#include "sysemu/cpu-throttle.h"
|
||||
#include "migration.h"
|
||||
#include "migration-stats.h"
|
||||
#include "trace.h"
|
||||
|
||||
/* vcpu throttling controls */
|
||||
static QEMUTimer *throttle_timer;
|
||||
static QEMUTimer *throttle_timer, *throttle_dirty_sync_timer;
|
||||
static unsigned int throttle_percentage;
|
||||
static bool throttle_dirty_sync_timer_active;
|
||||
static uint64_t throttle_dirty_sync_count_prev;
|
||||
|
||||
#define CPU_THROTTLE_PCT_MIN 1
|
||||
#define CPU_THROTTLE_PCT_MAX 99
|
||||
#define CPU_THROTTLE_TIMESLICE_NS 10000000
|
||||
|
||||
/* Making sure RAMBlock dirty bitmap is synchronized every five seconds */
|
||||
#define CPU_THROTTLE_DIRTY_SYNC_TIMESLICE_MS 5000
|
||||
|
||||
static void cpu_throttle_thread(CPUState *cpu, run_on_cpu_data opaque)
|
||||
{
|
||||
double pct;
|
||||
@ -112,6 +119,7 @@ void cpu_throttle_set(int new_throttle_pct)
|
||||
void cpu_throttle_stop(void)
|
||||
{
|
||||
qatomic_set(&throttle_percentage, 0);
|
||||
cpu_throttle_dirty_sync_timer(false);
|
||||
}
|
||||
|
||||
bool cpu_throttle_active(void)
|
||||
@ -124,8 +132,68 @@ int cpu_throttle_get_percentage(void)
|
||||
return qatomic_read(&throttle_percentage);
|
||||
}
|
||||
|
||||
void cpu_throttle_dirty_sync_timer_tick(void *opaque)
|
||||
{
|
||||
uint64_t sync_cnt = stat64_get(&mig_stats.dirty_sync_count);
|
||||
|
||||
/*
|
||||
* The first iteration copies all memory anyhow and has no
|
||||
* effect on guest performance, therefore omit it to avoid
|
||||
* paying extra for the sync penalty.
|
||||
*/
|
||||
if (sync_cnt <= 1) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (sync_cnt == throttle_dirty_sync_count_prev) {
|
||||
trace_cpu_throttle_dirty_sync();
|
||||
WITH_RCU_READ_LOCK_GUARD() {
|
||||
migration_bitmap_sync_precopy(false);
|
||||
}
|
||||
}
|
||||
|
||||
end:
|
||||
throttle_dirty_sync_count_prev = stat64_get(&mig_stats.dirty_sync_count);
|
||||
|
||||
timer_mod(throttle_dirty_sync_timer,
|
||||
qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL_RT) +
|
||||
CPU_THROTTLE_DIRTY_SYNC_TIMESLICE_MS);
|
||||
}
|
||||
|
||||
static bool cpu_throttle_dirty_sync_active(void)
|
||||
{
|
||||
return qatomic_read(&throttle_dirty_sync_timer_active);
|
||||
}
|
||||
|
||||
void cpu_throttle_dirty_sync_timer(bool enable)
|
||||
{
|
||||
assert(throttle_dirty_sync_timer);
|
||||
|
||||
if (enable) {
|
||||
if (!cpu_throttle_dirty_sync_active()) {
|
||||
/*
|
||||
* Always reset the dirty sync count cache, in case migration
|
||||
* was cancelled once.
|
||||
*/
|
||||
throttle_dirty_sync_count_prev = 0;
|
||||
timer_mod(throttle_dirty_sync_timer,
|
||||
qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL_RT) +
|
||||
CPU_THROTTLE_DIRTY_SYNC_TIMESLICE_MS);
|
||||
qatomic_set(&throttle_dirty_sync_timer_active, 1);
|
||||
}
|
||||
} else {
|
||||
if (cpu_throttle_dirty_sync_active()) {
|
||||
timer_del(throttle_dirty_sync_timer);
|
||||
qatomic_set(&throttle_dirty_sync_timer_active, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cpu_throttle_init(void)
|
||||
{
|
||||
throttle_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL_RT,
|
||||
cpu_throttle_timer_tick, NULL);
|
||||
throttle_dirty_sync_timer =
|
||||
timer_new_ms(QEMU_CLOCK_VIRTUAL_RT,
|
||||
cpu_throttle_dirty_sync_timer_tick, NULL);
|
||||
}
|
@ -29,6 +29,7 @@
|
||||
#include "sysemu/runstate.h"
|
||||
#include "exec/memory.h"
|
||||
#include "qemu/xxhash.h"
|
||||
#include "migration.h"
|
||||
|
||||
/*
|
||||
* total_dirty_pages is procted by BQL and is used
|
||||
@ -436,6 +437,7 @@ static void get_ramblock_dirty_info(RAMBlock *block,
|
||||
struct DirtyRateConfig *config)
|
||||
{
|
||||
uint64_t sample_pages_per_gigabytes = config->sample_pages_per_gigabytes;
|
||||
gsize len;
|
||||
|
||||
/* Right shift 30 bits to calc ramblock size in GB */
|
||||
info->sample_pages_count = (qemu_ram_get_used_length(block) *
|
||||
@ -444,7 +446,9 @@ static void get_ramblock_dirty_info(RAMBlock *block,
|
||||
info->ramblock_pages = qemu_ram_get_used_length(block) >>
|
||||
qemu_target_page_bits();
|
||||
info->ramblock_addr = qemu_ram_get_host_addr(block);
|
||||
strcpy(info->idstr, qemu_ram_get_idstr(block));
|
||||
len = g_strlcpy(info->idstr, qemu_ram_get_idstr(block),
|
||||
sizeof(info->idstr));
|
||||
g_assert(len < sizeof(info->idstr));
|
||||
}
|
||||
|
||||
static void free_ramblock_dirty_info(struct RamblockDirtyInfo *infos, int count)
|
||||
@ -839,8 +843,9 @@ void qmp_calc_dirty_rate(int64_t calc_time,
|
||||
|
||||
init_dirtyrate_stat(config);
|
||||
|
||||
qemu_thread_create(&thread, "get_dirtyrate", get_dirtyrate_thread,
|
||||
(void *)&config, QEMU_THREAD_DETACHED);
|
||||
qemu_thread_create(&thread, MIGRATION_THREAD_DIRTY_RATE,
|
||||
get_dirtyrate_thread, (void *)&config,
|
||||
QEMU_THREAD_DETACHED);
|
||||
}
|
||||
|
||||
|
||||
|
@ -13,6 +13,7 @@ system_ss.add(files(
|
||||
'block-dirty-bitmap.c',
|
||||
'channel.c',
|
||||
'channel-block.c',
|
||||
'cpu-throttle.c',
|
||||
'dirtyrate.c',
|
||||
'exec.c',
|
||||
'fd.c',
|
||||
|
@ -263,6 +263,9 @@ void migration_object_init(void)
|
||||
|
||||
ram_mig_init();
|
||||
dirty_bitmap_mig_init();
|
||||
|
||||
/* Initialize cpu throttle timers */
|
||||
cpu_throttle_init();
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
@ -1110,33 +1113,6 @@ void migrate_send_rp_resume_ack(MigrationIncomingState *mis, uint32_t value)
|
||||
migrate_send_rp_message(mis, MIG_RP_MSG_RESUME_ACK, sizeof(buf), &buf);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return true if we're already in the middle of a migration
|
||||
* (i.e. any of the active or setup states)
|
||||
*/
|
||||
bool migration_is_setup_or_active(void)
|
||||
{
|
||||
MigrationState *s = current_migration;
|
||||
|
||||
switch (s->state) {
|
||||
case MIGRATION_STATUS_ACTIVE:
|
||||
case MIGRATION_STATUS_POSTCOPY_ACTIVE:
|
||||
case MIGRATION_STATUS_POSTCOPY_PAUSED:
|
||||
case MIGRATION_STATUS_POSTCOPY_RECOVER_SETUP:
|
||||
case MIGRATION_STATUS_POSTCOPY_RECOVER:
|
||||
case MIGRATION_STATUS_SETUP:
|
||||
case MIGRATION_STATUS_PRE_SWITCHOVER:
|
||||
case MIGRATION_STATUS_DEVICE:
|
||||
case MIGRATION_STATUS_WAIT_UNPLUG:
|
||||
case MIGRATION_STATUS_COLO:
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
bool migration_is_running(void)
|
||||
{
|
||||
MigrationState *s = current_migration;
|
||||
@ -1152,11 +1128,10 @@ bool migration_is_running(void)
|
||||
case MIGRATION_STATUS_DEVICE:
|
||||
case MIGRATION_STATUS_WAIT_UNPLUG:
|
||||
case MIGRATION_STATUS_CANCELLING:
|
||||
case MIGRATION_STATUS_COLO:
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -1405,6 +1380,9 @@ void migrate_set_state(MigrationStatus *state, MigrationStatus old_state,
|
||||
static void migrate_fd_cleanup(MigrationState *s)
|
||||
{
|
||||
MigrationEventType type;
|
||||
QEMUFile *tmp = NULL;
|
||||
|
||||
trace_migrate_fd_cleanup();
|
||||
|
||||
g_free(s->hostname);
|
||||
s->hostname = NULL;
|
||||
@ -1415,26 +1393,29 @@ static void migrate_fd_cleanup(MigrationState *s)
|
||||
|
||||
close_return_path_on_source(s);
|
||||
|
||||
if (s->to_dst_file) {
|
||||
QEMUFile *tmp;
|
||||
|
||||
trace_migrate_fd_cleanup();
|
||||
if (s->migration_thread_running) {
|
||||
bql_unlock();
|
||||
if (s->migration_thread_running) {
|
||||
qemu_thread_join(&s->thread);
|
||||
s->migration_thread_running = false;
|
||||
}
|
||||
qemu_thread_join(&s->thread);
|
||||
s->migration_thread_running = false;
|
||||
bql_lock();
|
||||
}
|
||||
|
||||
multifd_send_shutdown();
|
||||
qemu_mutex_lock(&s->qemu_file_lock);
|
||||
WITH_QEMU_LOCK_GUARD(&s->qemu_file_lock) {
|
||||
/*
|
||||
* Close the file handle without the lock to make sure the critical
|
||||
* section won't block for long.
|
||||
*/
|
||||
tmp = s->to_dst_file;
|
||||
s->to_dst_file = NULL;
|
||||
qemu_mutex_unlock(&s->qemu_file_lock);
|
||||
}
|
||||
|
||||
if (tmp) {
|
||||
/*
|
||||
* Close the file handle without the lock to make sure the
|
||||
* critical section won't block for long.
|
||||
* We only need to shutdown multifd if tmp!=NULL, because if
|
||||
* tmp==NULL, it means the main channel isn't established, while
|
||||
* multifd is only setup after that (in migration_thread()).
|
||||
*/
|
||||
multifd_send_shutdown();
|
||||
migration_ioc_unregister_yank_from_file(tmp);
|
||||
qemu_fclose(tmp);
|
||||
}
|
||||
@ -1649,27 +1630,7 @@ bool migration_incoming_postcopy_advised(void)
|
||||
|
||||
bool migration_in_bg_snapshot(void)
|
||||
{
|
||||
return migrate_background_snapshot() &&
|
||||
migration_is_setup_or_active();
|
||||
}
|
||||
|
||||
bool migration_is_idle(void)
|
||||
{
|
||||
MigrationState *s = current_migration;
|
||||
|
||||
if (!s) {
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (s->state) {
|
||||
case MIGRATION_STATUS_NONE:
|
||||
case MIGRATION_STATUS_CANCELLED:
|
||||
case MIGRATION_STATUS_COMPLETED:
|
||||
case MIGRATION_STATUS_FAILED:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return migrate_background_snapshot() && migration_is_running();
|
||||
}
|
||||
|
||||
bool migration_is_active(void)
|
||||
@ -1750,7 +1711,7 @@ static bool is_busy(Error **reasonp, Error **errp)
|
||||
ERRP_GUARD();
|
||||
|
||||
/* Snapshots are similar to migrations, so check RUN_STATE_SAVE_VM too. */
|
||||
if (runstate_check(RUN_STATE_SAVE_VM) || !migration_is_idle()) {
|
||||
if (runstate_check(RUN_STATE_SAVE_VM) || migration_is_running()) {
|
||||
error_propagate_prepend(errp, *reasonp,
|
||||
"disallowing migration blocker "
|
||||
"(migration/snapshot in progress) for: ");
|
||||
@ -2323,7 +2284,7 @@ static void *source_return_path_thread(void *opaque)
|
||||
trace_source_return_path_thread_entry();
|
||||
rcu_register_thread();
|
||||
|
||||
while (migration_is_setup_or_active()) {
|
||||
while (migration_is_running()) {
|
||||
trace_source_return_path_thread_loop_top();
|
||||
|
||||
header_type = qemu_get_be16(rp);
|
||||
@ -2478,7 +2439,7 @@ static int open_return_path_on_source(MigrationState *ms)
|
||||
|
||||
trace_open_return_path_on_source();
|
||||
|
||||
qemu_thread_create(&ms->rp_state.rp_thread, "mig/src/rp-thr",
|
||||
qemu_thread_create(&ms->rp_state.rp_thread, MIGRATION_THREAD_SRC_RETURN,
|
||||
source_return_path_thread, ms, QEMU_THREAD_JOINABLE);
|
||||
ms->rp_state.rp_thread_created = true;
|
||||
|
||||
@ -3288,10 +3249,17 @@ static MigIterateState migration_iteration_run(MigrationState *s)
|
||||
|
||||
static void migration_iteration_finish(MigrationState *s)
|
||||
{
|
||||
/* If we enabled cpu throttling for auto-converge, turn it off. */
|
||||
cpu_throttle_stop();
|
||||
|
||||
bql_lock();
|
||||
|
||||
/*
|
||||
* If we enabled cpu throttling for auto-converge, turn it off.
|
||||
* Stopping CPU throttle should be serialized by BQL to avoid
|
||||
* racing for the throttle_dirty_sync_timer.
|
||||
*/
|
||||
if (migrate_auto_converge()) {
|
||||
cpu_throttle_stop();
|
||||
}
|
||||
|
||||
switch (s->state) {
|
||||
case MIGRATION_STATUS_COMPLETED:
|
||||
runstate_set(RUN_STATE_POSTMIGRATE);
|
||||
@ -3467,11 +3435,11 @@ static void *migration_thread(void *opaque)
|
||||
Error *local_err = NULL;
|
||||
int ret;
|
||||
|
||||
thread = migration_threads_add("live_migration", qemu_get_thread_id());
|
||||
thread = migration_threads_add(MIGRATION_THREAD_SRC_MAIN,
|
||||
qemu_get_thread_id());
|
||||
|
||||
rcu_register_thread();
|
||||
|
||||
object_ref(OBJECT(s));
|
||||
update_iteration_initial_status(s);
|
||||
|
||||
if (!multifd_send_setup()) {
|
||||
@ -3508,6 +3476,11 @@ static void *migration_thread(void *opaque)
|
||||
qemu_savevm_send_colo_enable(s->to_dst_file);
|
||||
}
|
||||
|
||||
if (migrate_auto_converge()) {
|
||||
/* Start RAMBlock dirty bitmap sync timer */
|
||||
cpu_throttle_dirty_sync_timer(true);
|
||||
}
|
||||
|
||||
bql_lock();
|
||||
ret = qemu_savevm_state_setup(s->to_dst_file, &local_err);
|
||||
bql_unlock();
|
||||
@ -3604,7 +3577,6 @@ static void *bg_migration_thread(void *opaque)
|
||||
int ret;
|
||||
|
||||
rcu_register_thread();
|
||||
object_ref(OBJECT(s));
|
||||
|
||||
migration_rate_set(RATE_LIMIT_DISABLED);
|
||||
|
||||
@ -3816,11 +3788,19 @@ void migrate_fd_connect(MigrationState *s, Error *error_in)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Take a refcount to make sure the migration object won't get freed by
|
||||
* the main thread already in migration_shutdown().
|
||||
*
|
||||
* The refcount will be released at the end of the thread function.
|
||||
*/
|
||||
object_ref(OBJECT(s));
|
||||
|
||||
if (migrate_background_snapshot()) {
|
||||
qemu_thread_create(&s->thread, "mig/snapshot",
|
||||
qemu_thread_create(&s->thread, MIGRATION_THREAD_SNAPSHOT,
|
||||
bg_migration_thread, s, QEMU_THREAD_JOINABLE);
|
||||
} else {
|
||||
qemu_thread_create(&s->thread, "mig/src/main",
|
||||
qemu_thread_create(&s->thread, MIGRATION_THREAD_SRC_MAIN,
|
||||
migration_thread, s, QEMU_THREAD_JOINABLE);
|
||||
}
|
||||
s->migration_thread_running = true;
|
||||
|
@ -28,6 +28,20 @@
|
||||
#include "sysemu/runstate.h"
|
||||
#include "migration/misc.h"
|
||||
|
||||
#define MIGRATION_THREAD_SNAPSHOT "mig/snapshot"
|
||||
#define MIGRATION_THREAD_DIRTY_RATE "mig/dirtyrate"
|
||||
|
||||
#define MIGRATION_THREAD_SRC_MAIN "mig/src/main"
|
||||
#define MIGRATION_THREAD_SRC_MULTIFD "mig/src/send_%d"
|
||||
#define MIGRATION_THREAD_SRC_RETURN "mig/src/return"
|
||||
#define MIGRATION_THREAD_SRC_TLS "mig/src/tls"
|
||||
|
||||
#define MIGRATION_THREAD_DST_COLO "mig/dst/colo"
|
||||
#define MIGRATION_THREAD_DST_MULTIFD "mig/src/recv_%d"
|
||||
#define MIGRATION_THREAD_DST_FAULT "mig/dst/fault"
|
||||
#define MIGRATION_THREAD_DST_LISTEN "mig/dst/listen"
|
||||
#define MIGRATION_THREAD_DST_PREEMPT "mig/dst/preempt"
|
||||
|
||||
struct PostcopyBlocktimeContext;
|
||||
|
||||
#define MIGRATION_RESUME_ACK_VALUE (1)
|
||||
@ -537,4 +551,9 @@ int migration_rp_wait(MigrationState *s);
|
||||
*/
|
||||
void migration_rp_kick(MigrationState *s);
|
||||
|
||||
void migration_bitmap_sync_precopy(bool last_stage);
|
||||
|
||||
/* migration/block-dirty-bitmap.c */
|
||||
void dirty_bitmap_mig_init(void);
|
||||
|
||||
#endif
|
||||
|
@ -600,6 +600,7 @@ static void *multifd_send_thread(void *opaque)
|
||||
* qatomic_store_release() in multifd_send().
|
||||
*/
|
||||
if (qatomic_load_acquire(&p->pending_job)) {
|
||||
p->flags = 0;
|
||||
p->iovs_num = 0;
|
||||
assert(!multifd_payload_empty(p->data));
|
||||
|
||||
@ -651,7 +652,6 @@ static void *multifd_send_thread(void *opaque)
|
||||
}
|
||||
/* p->next_packet_size will always be zero for a SYNC packet */
|
||||
stat64_add(&mig_stats.multifd_bytes, p->packet_len);
|
||||
p->flags = 0;
|
||||
}
|
||||
|
||||
qatomic_set(&p->pending_sync, false);
|
||||
@ -723,7 +723,7 @@ static bool multifd_tls_channel_connect(MultiFDSendParams *p,
|
||||
args->p = p;
|
||||
|
||||
p->tls_thread_created = true;
|
||||
qemu_thread_create(&p->tls_thread, "mig/src/tls",
|
||||
qemu_thread_create(&p->tls_thread, MIGRATION_THREAD_SRC_TLS,
|
||||
multifd_tls_handshake_thread, args,
|
||||
QEMU_THREAD_JOINABLE);
|
||||
return true;
|
||||
@ -841,7 +841,7 @@ bool multifd_send_setup(void)
|
||||
+ sizeof(uint64_t) * page_count;
|
||||
p->packet = g_malloc0(p->packet_len);
|
||||
}
|
||||
p->name = g_strdup_printf("mig/src/send_%d", i);
|
||||
p->name = g_strdup_printf(MIGRATION_THREAD_SRC_MULTIFD, i);
|
||||
p->write_flags = 0;
|
||||
|
||||
if (!multifd_new_send_channel_create(p, &local_err)) {
|
||||
@ -1259,7 +1259,7 @@ int multifd_recv_setup(Error **errp)
|
||||
+ sizeof(uint64_t) * page_count;
|
||||
p->packet = g_malloc0(p->packet_len);
|
||||
}
|
||||
p->name = g_strdup_printf("mig/dst/recv_%d", i);
|
||||
p->name = g_strdup_printf(MIGRATION_THREAD_DST_MULTIFD, i);
|
||||
p->normal = g_new0(ram_addr_t, page_count);
|
||||
p->zero = g_new0(ram_addr_t, page_count);
|
||||
}
|
||||
|
@ -1230,7 +1230,8 @@ int postcopy_ram_incoming_setup(MigrationIncomingState *mis)
|
||||
return -1;
|
||||
}
|
||||
|
||||
postcopy_thread_create(mis, &mis->fault_thread, "mig/dst/fault",
|
||||
postcopy_thread_create(mis, &mis->fault_thread,
|
||||
MIGRATION_THREAD_DST_FAULT,
|
||||
postcopy_ram_fault_thread, QEMU_THREAD_JOINABLE);
|
||||
mis->have_fault_thread = true;
|
||||
|
||||
@ -1250,7 +1251,8 @@ int postcopy_ram_incoming_setup(MigrationIncomingState *mis)
|
||||
* This thread needs to be created after the temp pages because
|
||||
* it'll fetch RAM_CHANNEL_POSTCOPY PostcopyTmpPage immediately.
|
||||
*/
|
||||
postcopy_thread_create(mis, &mis->postcopy_prio_thread, "mig/dst/preempt",
|
||||
postcopy_thread_create(mis, &mis->postcopy_prio_thread,
|
||||
MIGRATION_THREAD_DST_PREEMPT,
|
||||
postcopy_preempt_thread, QEMU_THREAD_JOINABLE);
|
||||
mis->preempt_thread_status = PREEMPT_THREAD_CREATED;
|
||||
}
|
||||
|
@ -1088,9 +1088,10 @@ static void migration_bitmap_sync(RAMState *rs, bool last_stage)
|
||||
}
|
||||
}
|
||||
|
||||
static void migration_bitmap_sync_precopy(RAMState *rs, bool last_stage)
|
||||
void migration_bitmap_sync_precopy(bool last_stage)
|
||||
{
|
||||
Error *local_err = NULL;
|
||||
assert(ram_state);
|
||||
|
||||
/*
|
||||
* The current notifier usage is just an optimization to migration, so we
|
||||
@ -1101,7 +1102,7 @@ static void migration_bitmap_sync_precopy(RAMState *rs, bool last_stage)
|
||||
local_err = NULL;
|
||||
}
|
||||
|
||||
migration_bitmap_sync(rs, last_stage);
|
||||
migration_bitmap_sync(ram_state, last_stage);
|
||||
|
||||
if (precopy_notify(PRECOPY_NOTIFY_AFTER_BITMAP_SYNC, &local_err)) {
|
||||
error_report_err(local_err);
|
||||
@ -2782,7 +2783,7 @@ static bool ram_init_bitmaps(RAMState *rs, Error **errp)
|
||||
if (!ret) {
|
||||
goto out_unlock;
|
||||
}
|
||||
migration_bitmap_sync_precopy(rs, false);
|
||||
migration_bitmap_sync_precopy(false);
|
||||
}
|
||||
}
|
||||
out_unlock:
|
||||
@ -2859,7 +2860,7 @@ void qemu_guest_free_page_hint(void *addr, size_t len)
|
||||
size_t used_len, start, npages;
|
||||
|
||||
/* This function is currently expected to be used during live migration */
|
||||
if (!migration_is_setup_or_active()) {
|
||||
if (!migration_is_running()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -3207,8 +3208,7 @@ static int ram_save_iterate(QEMUFile *f, void *opaque)
|
||||
}
|
||||
|
||||
out:
|
||||
if (ret >= 0
|
||||
&& migration_is_setup_or_active()) {
|
||||
if (ret >= 0 && migration_is_running()) {
|
||||
if (migrate_multifd() && migrate_multifd_flush_after_each_section() &&
|
||||
!migrate_mapped_ram()) {
|
||||
ret = multifd_ram_flush_and_sync();
|
||||
@ -3248,7 +3248,7 @@ static int ram_save_complete(QEMUFile *f, void *opaque)
|
||||
|
||||
WITH_RCU_READ_LOCK_GUARD() {
|
||||
if (!migration_in_postcopy()) {
|
||||
migration_bitmap_sync_precopy(rs, true);
|
||||
migration_bitmap_sync_precopy(true);
|
||||
}
|
||||
|
||||
ret = rdma_registration_start(f, RAM_CONTROL_FINISH);
|
||||
@ -3330,7 +3330,7 @@ static void ram_state_pending_exact(void *opaque, uint64_t *must_precopy,
|
||||
if (!migration_in_postcopy()) {
|
||||
bql_lock();
|
||||
WITH_RCU_READ_LOCK_GUARD() {
|
||||
migration_bitmap_sync_precopy(rs, false);
|
||||
migration_bitmap_sync_precopy(false);
|
||||
}
|
||||
bql_unlock();
|
||||
}
|
||||
@ -4294,6 +4294,7 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id)
|
||||
* it will be necessary to reduce the granularity of this
|
||||
* critical section.
|
||||
*/
|
||||
trace_ram_load_start();
|
||||
WITH_RCU_READ_LOCK_GUARD() {
|
||||
if (postcopy_running) {
|
||||
/*
|
||||
@ -4498,7 +4499,7 @@ static void ram_mig_ram_block_resized(RAMBlockNotifier *n, void *host,
|
||||
return;
|
||||
}
|
||||
|
||||
if (!migration_is_idle()) {
|
||||
if (migration_is_running()) {
|
||||
/*
|
||||
* Precopy code on the source cannot deal with the size of RAM blocks
|
||||
* changing at random points in time - especially after sending the
|
||||
|
@ -44,6 +44,7 @@ extern XBZRLECacheStats xbzrle_counters;
|
||||
INTERNAL_RAMBLOCK_FOREACH(block) \
|
||||
if (!qemu_ram_is_migratable(block)) {} else
|
||||
|
||||
void ram_mig_init(void);
|
||||
int xbzrle_cache_resize(uint64_t new_size, Error **errp);
|
||||
uint64_t ram_bytes_remaining(void);
|
||||
uint64_t ram_bytes_total(void);
|
||||
|
@ -2131,7 +2131,8 @@ static int loadvm_postcopy_handle_listen(MigrationIncomingState *mis)
|
||||
}
|
||||
|
||||
mis->have_listen_thread = true;
|
||||
postcopy_thread_create(mis, &mis->listen_thread, "mig/dst/listen",
|
||||
postcopy_thread_create(mis, &mis->listen_thread,
|
||||
MIGRATION_THREAD_DST_LISTEN,
|
||||
postcopy_ram_listen_thread, QEMU_THREAD_DETACHED);
|
||||
trace_loadvm_postcopy_handle_listen("return");
|
||||
|
||||
|
@ -115,6 +115,7 @@ colo_flush_ram_cache_end(void) ""
|
||||
save_xbzrle_page_skipping(void) ""
|
||||
save_xbzrle_page_overflow(void) ""
|
||||
ram_save_iterate_big_wait(uint64_t milliconds, int iterations) "big wait: %" PRIu64 " milliseconds, %d iterations"
|
||||
ram_load_start(void) ""
|
||||
ram_load_complete(int ret, uint64_t seq_iter) "exit_code %d seq iteration %" PRIu64
|
||||
ram_write_tracking_ramblock_start(const char *block_id, size_t page_size, void *addr, size_t length) "%s: page_size: %zu addr: %p length: %zu"
|
||||
ram_write_tracking_ramblock_stop(const char *block_id, size_t page_size, void *addr, size_t length) "%s: page_size: %zu addr: %p length: %zu"
|
||||
@ -378,3 +379,7 @@ migration_block_progression(unsigned percent) "Completed %u%%"
|
||||
# page_cache.c
|
||||
migration_pagecache_init(int64_t max_num_items) "Setting cache buckets to %" PRId64
|
||||
migration_pagecache_insert(void) "Error allocating page"
|
||||
|
||||
# cpu-throttle.c
|
||||
cpu_throttle_set(int new_throttle_pct) "set guest CPU throttled by %d%%"
|
||||
cpu_throttle_dirty_sync(void) ""
|
||||
|
@ -22,7 +22,8 @@
|
||||
#include "trace.h"
|
||||
|
||||
static int vmstate_subsection_save(QEMUFile *f, const VMStateDescription *vmsd,
|
||||
void *opaque, JSONWriter *vmdesc);
|
||||
void *opaque, JSONWriter *vmdesc,
|
||||
Error **errp);
|
||||
static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd,
|
||||
void *opaque);
|
||||
|
||||
@ -441,12 +442,13 @@ int vmstate_save_state_v(QEMUFile *f, const VMStateDescription *vmsd,
|
||||
json_writer_end_array(vmdesc);
|
||||
}
|
||||
|
||||
ret = vmstate_subsection_save(f, vmsd, opaque, vmdesc);
|
||||
ret = vmstate_subsection_save(f, vmsd, opaque, vmdesc, errp);
|
||||
|
||||
if (vmsd->post_save) {
|
||||
int ps_ret = vmsd->post_save(opaque);
|
||||
if (!ret) {
|
||||
if (!ret && ps_ret) {
|
||||
ret = ps_ret;
|
||||
error_setg(errp, "post-save failed: %s", vmsd->name);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
@ -518,7 +520,8 @@ static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd,
|
||||
}
|
||||
|
||||
static int vmstate_subsection_save(QEMUFile *f, const VMStateDescription *vmsd,
|
||||
void *opaque, JSONWriter *vmdesc)
|
||||
void *opaque, JSONWriter *vmdesc,
|
||||
Error **errp)
|
||||
{
|
||||
const VMStateDescription * const *sub = vmsd->subsections;
|
||||
bool vmdesc_has_subsections = false;
|
||||
@ -546,7 +549,7 @@ static int vmstate_subsection_save(QEMUFile *f, const VMStateDescription *vmsd,
|
||||
qemu_put_byte(f, len);
|
||||
qemu_put_buffer(f, (uint8_t *)vmsdsub->name, len);
|
||||
qemu_put_be32(f, vmsdsub->version_id);
|
||||
ret = vmstate_save_state(f, vmsdsub, opaque, vmdesc);
|
||||
ret = vmstate_save_state_with_err(f, vmsdsub, opaque, vmdesc, errp);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
@ -375,8 +375,7 @@ static int vhost_vdpa_net_data_start(NetClientState *nc)
|
||||
|
||||
assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_VDPA);
|
||||
|
||||
if (s->always_svq ||
|
||||
migration_is_setup_or_active()) {
|
||||
if (s->always_svq || migration_is_running()) {
|
||||
v->shadow_vqs_enabled = true;
|
||||
} else {
|
||||
v->shadow_vqs_enabled = false;
|
||||
|
@ -2284,12 +2284,17 @@
|
||||
#
|
||||
# Returns information of migration threads
|
||||
#
|
||||
# Features:
|
||||
#
|
||||
# @deprecated: This command is deprecated with no replacement yet.
|
||||
#
|
||||
# Returns: @MigrationThreadInfo
|
||||
#
|
||||
# Since: 7.2
|
||||
##
|
||||
{ 'command': 'query-migrationthreads',
|
||||
'returns': ['MigrationThreadInfo'] }
|
||||
'returns': ['MigrationThreadInfo'],
|
||||
'features': ['deprecated'] }
|
||||
|
||||
##
|
||||
# @snapshot-save:
|
||||
|
@ -35,7 +35,6 @@
|
||||
#include "sysemu/runstate.h"
|
||||
#include "hw/core/cpu.h"
|
||||
#include "sysemu/cpu-timers.h"
|
||||
#include "sysemu/cpu-throttle.h"
|
||||
#include "sysemu/cpu-timers-internal.h"
|
||||
|
||||
/* clock and ticks */
|
||||
@ -272,6 +271,4 @@ void cpu_timers_init(void)
|
||||
seqlock_init(&timers_state.vm_clock_seqlock);
|
||||
qemu_spin_init(&timers_state.vm_clock_lock);
|
||||
vmstate_register(NULL, 0, &vmstate_timers, &timers_state);
|
||||
|
||||
cpu_throttle_init();
|
||||
}
|
||||
|
@ -10,7 +10,6 @@ system_ss.add(files(
|
||||
'balloon.c',
|
||||
'bootdevice.c',
|
||||
'cpus.c',
|
||||
'cpu-throttle.c',
|
||||
'cpu-timers.c',
|
||||
'datadir.c',
|
||||
'dirtylimit.c',
|
||||
|
@ -679,7 +679,7 @@ DeviceState *qdev_device_add_from_qdict(const QDict *opts,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!migration_is_idle()) {
|
||||
if (migration_is_running()) {
|
||||
error_setg(errp, "device_add not allowed while migrating");
|
||||
return NULL;
|
||||
}
|
||||
@ -928,7 +928,7 @@ void qdev_unplug(DeviceState *dev, Error **errp)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!migration_is_idle() && !dev->allow_unplug_during_migration) {
|
||||
if (migration_is_running() && !dev->allow_unplug_during_migration) {
|
||||
error_setg(errp, "device_del not allowed while migrating");
|
||||
return;
|
||||
}
|
||||
|
@ -44,6 +44,3 @@ dirtylimit_state_finalize(void)
|
||||
dirtylimit_throttle_pct(int cpu_index, uint64_t pct, int64_t time_us) "CPU[%d] throttle percent: %" PRIu64 ", throttle adjust time %"PRIi64 " us"
|
||||
dirtylimit_set_vcpu(int cpu_index, uint64_t quota) "CPU[%d] set dirty page rate limit %"PRIu64
|
||||
dirtylimit_vcpu_execute(int cpu_index, int64_t sleep_time_us) "CPU[%d] sleep %"PRIi64 " us"
|
||||
|
||||
# cpu-throttle.c
|
||||
cpu_throttle_set(int new_throttle_pct) "set guest CPU throttled by %d%%"
|
||||
|
@ -2791,6 +2791,8 @@ static void test_migrate_auto_converge(void)
|
||||
* so we need to decrease a bandwidth.
|
||||
*/
|
||||
const int64_t init_pct = 5, inc_pct = 25, max_pct = 95;
|
||||
uint64_t prev_dirty_sync_cnt, dirty_sync_cnt;
|
||||
int max_try_count, hit = 0;
|
||||
|
||||
if (test_migrate_start(&from, &to, uri, &args)) {
|
||||
return;
|
||||
@ -2827,6 +2829,36 @@ static void test_migrate_auto_converge(void)
|
||||
} while (true);
|
||||
/* The first percentage of throttling should be at least init_pct */
|
||||
g_assert_cmpint(percentage, >=, init_pct);
|
||||
|
||||
/*
|
||||
* End the loop when the dirty sync count greater than 1.
|
||||
*/
|
||||
while ((dirty_sync_cnt = get_migration_pass(from)) < 2) {
|
||||
usleep(1000 * 1000);
|
||||
}
|
||||
|
||||
prev_dirty_sync_cnt = dirty_sync_cnt;
|
||||
|
||||
/*
|
||||
* The RAMBlock dirty sync count must changes in 5 seconds, here we set
|
||||
* the timeout to 10 seconds to ensure it changes.
|
||||
*
|
||||
* Note that migrate_ensure_non_converge set the max-bandwidth to 3MB/s,
|
||||
* while the qtest mem is >= 100MB, one iteration takes at least 33s (100/3)
|
||||
* to complete; this ensures that the RAMBlock dirty sync occurs.
|
||||
*/
|
||||
max_try_count = 10;
|
||||
while (--max_try_count) {
|
||||
dirty_sync_cnt = get_migration_pass(from);
|
||||
if (dirty_sync_cnt != prev_dirty_sync_cnt) {
|
||||
hit = 1;
|
||||
break;
|
||||
}
|
||||
prev_dirty_sync_cnt = dirty_sync_cnt;
|
||||
sleep(1);
|
||||
}
|
||||
g_assert_cmpint(hit, ==, 1);
|
||||
|
||||
/* Now, when we tested that throttling works, let it converge */
|
||||
migrate_ensure_converge(from);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user