Merge tag 'pull-migration-20220720c' of https://gitlab.com/dagrh/qemu into staging

Migration pull 2022-07-20

This replaces yesterdays pull and:
  a) Fixes some test build errors without TLS
  b) Reenabled the zlib acceleration on s390
     now that we have Ilya's fix

  Hyman's dirty page rate limit set
  Ilya's fix for zlib vs migration
  Peter's postcopy-preempt
  Cleanup from Dan
  zero-copy tidy ups from Leo
  multifd doc fix from Juan
  Revert disable of zlib acceleration on s390x

Signed-off-by: Dr. David Alan Gilbert <dgilbert@redhat.com>

# gpg: Signature made Wed 20 Jul 2022 12:18:56 BST
# gpg:                using RSA key 45F5C71B4A0CB7FB977A9FA90516331EBC5BFDE7
# gpg: Good signature from "Dr. David Alan Gilbert (RH2) <dgilbert@redhat.com>" [full]
# Primary key fingerprint: 45F5 C71B 4A0C B7FB 977A  9FA9 0516 331E BC5B FDE7

* tag 'pull-migration-20220720c' of https://gitlab.com/dagrh/qemu: (30 commits)
  Revert "gitlab: disable accelerated zlib for s390x"
  migration: Avoid false-positive on non-supported scenarios for zero-copy-send
  multifd: Document the locking of MultiFD{Send/Recv}Params
  migration/multifd: Report to user when zerocopy not working
  Add dirty-sync-missed-zero-copy migration stat
  QIOChannelSocket: Fix zero-copy flush returning code 1 when nothing sent
  migration: remove unreachable code after reading data
  tests: Add postcopy preempt tests
  tests: Add postcopy tls recovery migration test
  tests: Add postcopy tls migration test
  tests: Move MigrateCommon upper
  migration: Respect postcopy request order in preemption mode
  migration: Enable TLS for preempt channel
  migration: Export tls-[creds|hostname|authz] params to cmdline too
  migration: Add helpers to detect TLS capability
  migration: Add property x-postcopy-preempt-break-huge
  migration: Create the postcopy preempt channel asynchronously
  migration: Postcopy recover with preempt enabled
  migration: Postcopy preemption enablement
  migration: Postcopy preemption preparation on channel creation
  ...

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell
2022-07-20 22:33:35 +01:00
44 changed files with 2406 additions and 307 deletions
@@ -8,8 +8,6 @@ ubuntu-20.04-s390x-all-linux-static:
tags:
- ubuntu_20.04
- s390x
variables:
DFLTCC: 0
rules:
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
- if: "$S390X_RUNNER_AVAILABLE"
@@ -29,8 +27,6 @@ ubuntu-20.04-s390x-all:
tags:
- ubuntu_20.04
- s390x
variables:
DFLTCC: 0
timeout: 75m
rules:
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
@@ -48,8 +44,6 @@ ubuntu-20.04-s390x-alldbg:
tags:
- ubuntu_20.04
- s390x
variables:
DFLTCC: 0
rules:
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
when: manual
@@ -71,8 +65,6 @@ ubuntu-20.04-s390x-clang:
tags:
- ubuntu_20.04
- s390x
variables:
DFLTCC: 0
rules:
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
when: manual
@@ -93,8 +85,6 @@ ubuntu-20.04-s390x-tci:
tags:
- ubuntu_20.04
- s390x
variables:
DFLTCC: 0
rules:
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
when: manual
@@ -114,8 +104,6 @@ ubuntu-20.04-s390x-notcg:
tags:
- ubuntu_20.04
- s390x
variables:
DFLTCC: 0
rules:
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
when: manual
+2 -4
View File
@@ -218,7 +218,6 @@ jobs:
- TEST_CMD="make check check-tcg V=1"
- CONFIG="--disable-containers --target-list=${MAIN_SOFTMMU_TARGETS},s390x-linux-user"
- UNRELIABLE=true
- DFLTCC=0
script:
- BUILD_RC=0 && make -j${JOBS} || BUILD_RC=$?
- |
@@ -258,7 +257,7 @@ jobs:
env:
- CONFIG="--disable-containers --audio-drv-list=sdl --disable-user
--target-list-exclude=${MAIN_SOFTMMU_TARGETS}"
- DFLTCC=0
- name: "[s390x] GCC (user)"
arch: s390x
dist: focal
@@ -270,7 +269,7 @@ jobs:
- ninja-build
env:
- CONFIG="--disable-containers --disable-system"
- DFLTCC=0
- name: "[s390x] Clang (disable-tcg)"
arch: s390x
dist: focal
@@ -304,4 +303,3 @@ jobs:
- CONFIG="--disable-containers --disable-tcg --enable-kvm
--disable-tools --host-cc=clang --cxx=clang++"
- UNRELIABLE=true
- DFLTCC=0
+36 -10
View File
@@ -45,6 +45,7 @@
#include "qemu/guest-random.h"
#include "sysemu/hw_accel.h"
#include "kvm-cpus.h"
#include "sysemu/dirtylimit.h"
#include "hw/boards.h"
#include "monitor/stats.h"
@@ -477,6 +478,7 @@ int kvm_init_vcpu(CPUState *cpu, Error **errp)
cpu->kvm_state = s;
cpu->vcpu_dirty = true;
cpu->dirty_pages = 0;
cpu->throttle_us_per_full = 0;
mmap_size = kvm_ioctl(s, KVM_GET_VCPU_MMAP_SIZE, 0);
if (mmap_size < 0) {
@@ -757,17 +759,20 @@ static uint32_t kvm_dirty_ring_reap_one(KVMState *s, CPUState *cpu)
}
/* Must be with slots_lock held */
static uint64_t kvm_dirty_ring_reap_locked(KVMState *s)
static uint64_t kvm_dirty_ring_reap_locked(KVMState *s, CPUState* cpu)
{
int ret;
CPUState *cpu;
uint64_t total = 0;
int64_t stamp;
stamp = get_clock();
CPU_FOREACH(cpu) {
total += kvm_dirty_ring_reap_one(s, cpu);
if (cpu) {
total = kvm_dirty_ring_reap_one(s, cpu);
} else {
CPU_FOREACH(cpu) {
total += kvm_dirty_ring_reap_one(s, cpu);
}
}
if (total) {
@@ -788,7 +793,7 @@ static uint64_t kvm_dirty_ring_reap_locked(KVMState *s)
* Currently for simplicity, we must hold BQL before calling this. We can
* consider to drop the BQL if we're clear with all the race conditions.
*/
static uint64_t kvm_dirty_ring_reap(KVMState *s)
static uint64_t kvm_dirty_ring_reap(KVMState *s, CPUState *cpu)
{
uint64_t total;
@@ -808,7 +813,7 @@ static uint64_t kvm_dirty_ring_reap(KVMState *s)
* reset below.
*/
kvm_slots_lock();
total = kvm_dirty_ring_reap_locked(s);
total = kvm_dirty_ring_reap_locked(s, cpu);
kvm_slots_unlock();
return total;
@@ -855,7 +860,7 @@ static void kvm_dirty_ring_flush(void)
* vcpus out in a synchronous way.
*/
kvm_cpu_synchronize_kick_all();
kvm_dirty_ring_reap(kvm_state);
kvm_dirty_ring_reap(kvm_state, NULL);
trace_kvm_dirty_ring_flush(1);
}
@@ -1399,7 +1404,7 @@ static void kvm_set_phys_mem(KVMMemoryListener *kml,
* Not easy. Let's cross the fingers until it's fixed.
*/
if (kvm_state->kvm_dirty_ring_size) {
kvm_dirty_ring_reap_locked(kvm_state);
kvm_dirty_ring_reap_locked(kvm_state, NULL);
} else {
kvm_slot_get_dirty_log(kvm_state, mem);
}
@@ -1467,11 +1472,16 @@ static void *kvm_dirty_ring_reaper_thread(void *data)
*/
sleep(1);
/* keep sleeping so that dirtylimit not be interfered by reaper */
if (dirtylimit_in_service()) {
continue;
}
trace_kvm_dirty_ring_reaper("wakeup");
r->reaper_state = KVM_DIRTY_RING_REAPER_REAPING;
qemu_mutex_lock_iothread();
kvm_dirty_ring_reap(s);
kvm_dirty_ring_reap(s, NULL);
qemu_mutex_unlock_iothread();
r->reaper_iteration++;
@@ -2315,6 +2325,11 @@ static void query_stats_cb(StatsResultList **result, StatsTarget target,
strList *names, strList *targets, Error **errp);
static void query_stats_schemas_cb(StatsSchemaList **result, Error **errp);
uint32_t kvm_dirty_ring_size(void)
{
return kvm_state->kvm_dirty_ring_size;
}
static int kvm_init(MachineState *ms)
{
MachineClass *mc = MACHINE_GET_CLASS(ms);
@@ -2967,8 +2982,19 @@ int kvm_cpu_exec(CPUState *cpu)
*/
trace_kvm_dirty_ring_full(cpu->cpu_index);
qemu_mutex_lock_iothread();
kvm_dirty_ring_reap(kvm_state);
/*
* We throttle vCPU by making it sleep once it exit from kernel
* due to dirty ring full. In the dirtylimit scenario, reaping
* all vCPUs after a single vCPU dirty ring get full result in
* the miss of sleep, so just reap the ring-fulled vCPU.
*/
if (dirtylimit_in_service()) {
kvm_dirty_ring_reap(kvm_state, cpu);
} else {
kvm_dirty_ring_reap(kvm_state, NULL);
}
qemu_mutex_unlock_iothread();
dirtylimit_vcpu_execute(cpu);
ret = 0;
break;
case KVM_EXIT_SYSTEM_EVENT:
+5
View File
@@ -148,3 +148,8 @@ bool kvm_dirty_ring_enabled(void)
{
return false;
}
uint32_t kvm_dirty_ring_size(void)
{
return 0;
}
+8
View File
@@ -73,6 +73,12 @@ static int cpu_get_free_index(void)
}
CPUTailQ cpus = QTAILQ_HEAD_INITIALIZER(cpus);
static unsigned int cpu_list_generation_id;
unsigned int cpu_list_generation_id_get(void)
{
return cpu_list_generation_id;
}
void cpu_list_add(CPUState *cpu)
{
@@ -84,6 +90,7 @@ void cpu_list_add(CPUState *cpu)
assert(!cpu_index_auto_assigned);
}
QTAILQ_INSERT_TAIL_RCU(&cpus, cpu, node);
cpu_list_generation_id++;
}
void cpu_list_remove(CPUState *cpu)
@@ -96,6 +103,7 @@ void cpu_list_remove(CPUState *cpu)
QTAILQ_REMOVE_RCU(&cpus, cpu, node);
cpu->cpu_index = UNASSIGNED_CPU_INDEX;
cpu_list_generation_id++;
}
CPUState *qemu_get_cpu(int index)
+13
View File
@@ -865,6 +865,19 @@ SRST
Display the vcpu dirty rate information.
ERST
{
.name = "vcpu_dirty_limit",
.args_type = "",
.params = "",
.help = "show dirty page limit information of all vCPU",
.cmd = hmp_info_vcpu_dirty_limit,
},
SRST
``info vcpu_dirty_limit``
Display the vcpu dirty page limit information.
ERST
#if defined(TARGET_I386)
{
.name = "sgx",
+32
View File
@@ -1768,3 +1768,35 @@ ERST
"\n\t\t\t -b to specify dirty bitmap as method of calculation)",
.cmd = hmp_calc_dirty_rate,
},
SRST
``set_vcpu_dirty_limit``
Set dirty page rate limit on virtual CPU, the information about all the
virtual CPU dirty limit status can be observed with ``info vcpu_dirty_limit``
command.
ERST
{
.name = "set_vcpu_dirty_limit",
.args_type = "dirty_rate:l,cpu_index:l?",
.params = "dirty_rate [cpu_index]",
.help = "set dirty page rate limit, use cpu_index to set limit"
"\n\t\t\t\t\t on a specified virtual cpu",
.cmd = hmp_set_vcpu_dirty_limit,
},
SRST
``cancel_vcpu_dirty_limit``
Cancel dirty page rate limit on virtual CPU, the information about all the
virtual CPU dirty limit status can be observed with ``info vcpu_dirty_limit``
command.
ERST
{
.name = "cancel_vcpu_dirty_limit",
.args_type = "cpu_index:l?",
.params = "[cpu_index]",
.help = "cancel dirty page rate limit, use cpu_index to cancel"
"\n\t\t\t\t\t limit on a specified virtual cpu",
.cmd = hmp_cancel_vcpu_dirty_limit,
},
+1
View File
@@ -35,6 +35,7 @@ extern intptr_t qemu_host_page_mask;
void qemu_init_cpu_list(void);
void cpu_list_lock(void);
void cpu_list_unlock(void);
unsigned int cpu_list_generation_id_get(void);
void tcg_flush_softmmu_tlb(CPUState *cs);
+4 -1
View File
@@ -69,7 +69,10 @@ static inline void fuzz_dma_read_cb(size_t addr,
/* Dirty tracking enabled because measuring dirty rate */
#define GLOBAL_DIRTY_DIRTY_RATE (1U << 1)
#define GLOBAL_DIRTY_MASK (0x3)
/* Dirty tracking enabled because dirty limit */
#define GLOBAL_DIRTY_LIMIT (1U << 2)
#define GLOBAL_DIRTY_MASK (0x7)
extern unsigned int global_dirty_tracking;
+6
View File
@@ -418,6 +418,12 @@ struct CPUState {
*/
bool throttle_thread_scheduled;
/*
* Sleep throttle_us_per_full microseconds once dirty ring is full
* if dirty page rate limit is enabled.
*/
int64_t throttle_us_per_full;
bool ignore_memory_transaction_failures;
/* Used for user-only emulation of prctl(PR_SET_UNALIGN). */
+3
View File
@@ -131,6 +131,9 @@ void hmp_replay_delete_break(Monitor *mon, const QDict *qdict);
void hmp_replay_seek(Monitor *mon, const QDict *qdict);
void hmp_info_dirty_rate(Monitor *mon, const QDict *qdict);
void hmp_calc_dirty_rate(Monitor *mon, const QDict *qdict);
void hmp_set_vcpu_dirty_limit(Monitor *mon, const QDict *qdict);
void hmp_cancel_vcpu_dirty_limit(Monitor *mon, const QDict *qdict);
void hmp_info_vcpu_dirty_limit(Monitor *mon, const QDict *qdict);
void hmp_human_readable_text_helper(Monitor *mon,
HumanReadableText *(*qmp_handler)(Error **));
void hmp_info_stats(Monitor *mon, const QDict *qdict);
+37
View File
@@ -0,0 +1,37 @@
/*
* Dirty page rate limit common functions
*
* Copyright (c) 2022 CHINA TELECOM CO.,LTD.
*
* Authors:
* Hyman Huang() <huangy81@chinatelecom.cn>
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*/
#ifndef QEMU_DIRTYRLIMIT_H
#define QEMU_DIRTYRLIMIT_H
#define DIRTYLIMIT_CALC_TIME_MS 1000 /* 1000ms */
int64_t vcpu_dirty_rate_get(int cpu_index);
void vcpu_dirty_rate_stat_start(void);
void vcpu_dirty_rate_stat_stop(void);
void vcpu_dirty_rate_stat_initialize(void);
void vcpu_dirty_rate_stat_finalize(void);
void dirtylimit_state_lock(void);
void dirtylimit_state_unlock(void);
void dirtylimit_state_initialize(void);
void dirtylimit_state_finalize(void);
bool dirtylimit_in_service(void);
bool dirtylimit_vcpu_index_valid(int cpu_index);
void dirtylimit_process(void);
void dirtylimit_change(bool start);
void dirtylimit_set_vcpu(int cpu_index,
uint64_t quota,
bool enable);
void dirtylimit_set_all(uint64_t quota,
bool enable);
void dirtylimit_vcpu_execute(CPUState *cpu);
#endif
+28
View File
@@ -0,0 +1,28 @@
/*
* dirty page rate helper functions
*
* Copyright (c) 2022 CHINA TELECOM CO.,LTD.
*
* Authors:
* Hyman Huang() <huangy81@chinatelecom.cn>
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*/
#ifndef QEMU_DIRTYRATE_H
#define QEMU_DIRTYRATE_H
typedef struct VcpuStat {
int nvcpu; /* number of vcpu */
DirtyRateVcpu *rates; /* array of dirty rate for each vcpu */
} VcpuStat;
int64_t vcpu_calculate_dirtyrate(int64_t calc_time_ms,
VcpuStat *stat,
unsigned int flag,
bool one_shot);
void global_dirty_log_change(unsigned int flag,
bool start);
#endif
+2
View File
@@ -582,4 +582,6 @@ bool kvm_cpu_check_are_resettable(void);
bool kvm_arch_cpu_check_are_resettable(void);
bool kvm_dirty_ring_enabled(void);
uint32_t kvm_dirty_ring_size(void);
#endif
+7 -1
View File
@@ -716,12 +716,18 @@ static int qio_channel_socket_flush(QIOChannel *ioc,
struct cmsghdr *cm;
char control[CMSG_SPACE(sizeof(*serr))];
int received;
int ret = 1;
int ret;
if (sioc->zero_copy_queued == sioc->zero_copy_sent) {
return 0;
}
msg.msg_control = control;
msg.msg_controllen = sizeof(control);
memset(control, 0, sizeof(control));
ret = 1;
while (sioc->zero_copy_sent < sioc->zero_copy_queued) {
received = recvmsg(sioc->fd, &msg, MSG_ERRQUEUE);
if (received < 0) {
+2 -7
View File
@@ -38,9 +38,7 @@ void migration_channel_process_incoming(QIOChannel *ioc)
trace_migration_set_incoming_channel(
ioc, object_get_typename(OBJECT(ioc)));
if (migrate_use_tls() &&
!object_dynamic_cast(OBJECT(ioc),
TYPE_QIO_CHANNEL_TLS)) {
if (migrate_channel_requires_tls_upgrade(ioc)) {
migration_tls_channel_process_incoming(s, ioc, &local_err);
} else {
migration_ioc_register_yank(ioc);
@@ -70,10 +68,7 @@ void migration_channel_connect(MigrationState *s,
ioc, object_get_typename(OBJECT(ioc)), hostname, error);
if (!error) {
if (s->parameters.tls_creds &&
*s->parameters.tls_creds &&
!object_dynamic_cast(OBJECT(ioc),
TYPE_QIO_CHANNEL_TLS)) {
if (migrate_channel_requires_tls_upgrade(ioc)) {
migration_tls_channel_connect(s, ioc, hostname, &error);
if (!error) {
+144 -83
View File
@@ -46,7 +46,7 @@ static struct DirtyRateStat DirtyStat;
static DirtyRateMeasureMode dirtyrate_mode =
DIRTY_RATE_MEASURE_MODE_PAGE_SAMPLING;
static int64_t set_sample_page_period(int64_t msec, int64_t initial_time)
static int64_t dirty_stat_wait(int64_t msec, int64_t initial_time)
{
int64_t current_time;
@@ -60,6 +60,132 @@ static int64_t set_sample_page_period(int64_t msec, int64_t initial_time)
return msec;
}
static inline void record_dirtypages(DirtyPageRecord *dirty_pages,
CPUState *cpu, bool start)
{
if (start) {
dirty_pages[cpu->cpu_index].start_pages = cpu->dirty_pages;
} else {
dirty_pages[cpu->cpu_index].end_pages = cpu->dirty_pages;
}
}
static int64_t do_calculate_dirtyrate(DirtyPageRecord dirty_pages,
int64_t calc_time_ms)
{
uint64_t memory_size_MB;
uint64_t increased_dirty_pages =
dirty_pages.end_pages - dirty_pages.start_pages;
memory_size_MB = (increased_dirty_pages * TARGET_PAGE_SIZE) >> 20;
return memory_size_MB * 1000 / calc_time_ms;
}
void global_dirty_log_change(unsigned int flag, bool start)
{
qemu_mutex_lock_iothread();
if (start) {
memory_global_dirty_log_start(flag);
} else {
memory_global_dirty_log_stop(flag);
}
qemu_mutex_unlock_iothread();
}
/*
* global_dirty_log_sync
* 1. sync dirty log from kvm
* 2. stop dirty tracking if needed.
*/
static void global_dirty_log_sync(unsigned int flag, bool one_shot)
{
qemu_mutex_lock_iothread();
memory_global_dirty_log_sync();
if (one_shot) {
memory_global_dirty_log_stop(flag);
}
qemu_mutex_unlock_iothread();
}
static DirtyPageRecord *vcpu_dirty_stat_alloc(VcpuStat *stat)
{
CPUState *cpu;
DirtyPageRecord *records;
int nvcpu = 0;
CPU_FOREACH(cpu) {
nvcpu++;
}
stat->nvcpu = nvcpu;
stat->rates = g_malloc0(sizeof(DirtyRateVcpu) * nvcpu);
records = g_malloc0(sizeof(DirtyPageRecord) * nvcpu);
return records;
}
static void vcpu_dirty_stat_collect(VcpuStat *stat,
DirtyPageRecord *records,
bool start)
{
CPUState *cpu;
CPU_FOREACH(cpu) {
record_dirtypages(records, cpu, start);
}
}
int64_t vcpu_calculate_dirtyrate(int64_t calc_time_ms,
VcpuStat *stat,
unsigned int flag,
bool one_shot)
{
DirtyPageRecord *records;
int64_t init_time_ms;
int64_t duration;
int64_t dirtyrate;
int i = 0;
unsigned int gen_id;
retry:
init_time_ms = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
cpu_list_lock();
gen_id = cpu_list_generation_id_get();
records = vcpu_dirty_stat_alloc(stat);
vcpu_dirty_stat_collect(stat, records, true);
cpu_list_unlock();
duration = dirty_stat_wait(calc_time_ms, init_time_ms);
global_dirty_log_sync(flag, one_shot);
cpu_list_lock();
if (gen_id != cpu_list_generation_id_get()) {
g_free(records);
g_free(stat->rates);
cpu_list_unlock();
goto retry;
}
vcpu_dirty_stat_collect(stat, records, false);
cpu_list_unlock();
for (i = 0; i < stat->nvcpu; i++) {
dirtyrate = do_calculate_dirtyrate(records[i], duration);
stat->rates[i].id = i;
stat->rates[i].dirty_rate = dirtyrate;
trace_dirtyrate_do_calculate_vcpu(i, dirtyrate);
}
g_free(records);
return duration;
}
static bool is_sample_period_valid(int64_t sec)
{
if (sec < MIN_FETCH_DIRTYRATE_TIME_SEC ||
@@ -396,44 +522,6 @@ static bool compare_page_hash_info(struct RamblockDirtyInfo *info,
return true;
}
static inline void record_dirtypages(DirtyPageRecord *dirty_pages,
CPUState *cpu, bool start)
{
if (start) {
dirty_pages[cpu->cpu_index].start_pages = cpu->dirty_pages;
} else {
dirty_pages[cpu->cpu_index].end_pages = cpu->dirty_pages;
}
}
static void dirtyrate_global_dirty_log_start(void)
{
qemu_mutex_lock_iothread();
memory_global_dirty_log_start(GLOBAL_DIRTY_DIRTY_RATE);
qemu_mutex_unlock_iothread();
}
static void dirtyrate_global_dirty_log_stop(void)
{
qemu_mutex_lock_iothread();
memory_global_dirty_log_sync();
memory_global_dirty_log_stop(GLOBAL_DIRTY_DIRTY_RATE);
qemu_mutex_unlock_iothread();
}
static int64_t do_calculate_dirtyrate_vcpu(DirtyPageRecord dirty_pages)
{
uint64_t memory_size_MB;
int64_t time_s;
uint64_t increased_dirty_pages =
dirty_pages.end_pages - dirty_pages.start_pages;
memory_size_MB = (increased_dirty_pages * TARGET_PAGE_SIZE) >> 20;
time_s = DirtyStat.calc_time;
return memory_size_MB / time_s;
}
static inline void record_dirtypages_bitmap(DirtyPageRecord *dirty_pages,
bool start)
{
@@ -444,11 +532,6 @@ static inline void record_dirtypages_bitmap(DirtyPageRecord *dirty_pages,
}
}
static void do_calculate_dirtyrate_bitmap(DirtyPageRecord dirty_pages)
{
DirtyStat.dirty_rate = do_calculate_dirtyrate_vcpu(dirty_pages);
}
static inline void dirtyrate_manual_reset_protect(void)
{
RAMBlock *block = NULL;
@@ -492,71 +575,49 @@ static void calculate_dirtyrate_dirty_bitmap(struct DirtyRateConfig config)
DirtyStat.start_time = start_time / 1000;
msec = config.sample_period_seconds * 1000;
msec = set_sample_page_period(msec, start_time);
msec = dirty_stat_wait(msec, start_time);
DirtyStat.calc_time = msec / 1000;
/*
* dirtyrate_global_dirty_log_stop do two things.
* do two things.
* 1. fetch dirty bitmap from kvm
* 2. stop dirty tracking
*/
dirtyrate_global_dirty_log_stop();
global_dirty_log_sync(GLOBAL_DIRTY_DIRTY_RATE, true);
record_dirtypages_bitmap(&dirty_pages, false);
do_calculate_dirtyrate_bitmap(dirty_pages);
DirtyStat.dirty_rate = do_calculate_dirtyrate(dirty_pages, msec);
}
static void calculate_dirtyrate_dirty_ring(struct DirtyRateConfig config)
{
CPUState *cpu;
int64_t msec = 0;
int64_t start_time;
int64_t duration;
uint64_t dirtyrate = 0;
uint64_t dirtyrate_sum = 0;
DirtyPageRecord *dirty_pages;
int nvcpu = 0;
int i = 0;
CPU_FOREACH(cpu) {
nvcpu++;
}
/* start log sync */
global_dirty_log_change(GLOBAL_DIRTY_DIRTY_RATE, true);
dirty_pages = malloc(sizeof(*dirty_pages) * nvcpu);
DirtyStat.start_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME) / 1000;
DirtyStat.dirty_ring.nvcpu = nvcpu;
DirtyStat.dirty_ring.rates = malloc(sizeof(DirtyRateVcpu) * nvcpu);
/* calculate vcpu dirtyrate */
duration = vcpu_calculate_dirtyrate(config.sample_period_seconds * 1000,
&DirtyStat.dirty_ring,
GLOBAL_DIRTY_DIRTY_RATE,
true);
dirtyrate_global_dirty_log_start();
CPU_FOREACH(cpu) {
record_dirtypages(dirty_pages, cpu, true);
}
start_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
DirtyStat.start_time = start_time / 1000;
msec = config.sample_period_seconds * 1000;
msec = set_sample_page_period(msec, start_time);
DirtyStat.calc_time = msec / 1000;
dirtyrate_global_dirty_log_stop();
CPU_FOREACH(cpu) {
record_dirtypages(dirty_pages, cpu, false);
}
DirtyStat.calc_time = duration / 1000;
/* calculate vm dirtyrate */
for (i = 0; i < DirtyStat.dirty_ring.nvcpu; i++) {
dirtyrate = do_calculate_dirtyrate_vcpu(dirty_pages[i]);
trace_dirtyrate_do_calculate_vcpu(i, dirtyrate);
DirtyStat.dirty_ring.rates[i].id = i;
dirtyrate = DirtyStat.dirty_ring.rates[i].dirty_rate;
DirtyStat.dirty_ring.rates[i].dirty_rate = dirtyrate;
dirtyrate_sum += dirtyrate;
}
DirtyStat.dirty_rate = dirtyrate_sum;
free(dirty_pages);
}
static void calculate_dirtyrate_sample_vm(struct DirtyRateConfig config)
@@ -574,7 +635,7 @@ static void calculate_dirtyrate_sample_vm(struct DirtyRateConfig config)
rcu_read_unlock();
msec = config.sample_period_seconds * 1000;
msec = set_sample_page_period(msec, initial_time);
msec = dirty_stat_wait(msec, initial_time);
DirtyStat.start_time = initial_time / 1000;
DirtyStat.calc_time = msec / 1000;
+2 -5
View File
@@ -13,6 +13,8 @@
#ifndef QEMU_MIGRATION_DIRTYRATE_H
#define QEMU_MIGRATION_DIRTYRATE_H
#include "sysemu/dirtyrate.h"
/*
* Sample 512 pages per GB as default.
*/
@@ -65,11 +67,6 @@ typedef struct SampleVMStat {
uint64_t total_block_mem_MB; /* size of total sampled pages in MB */
} SampleVMStat;
typedef struct VcpuStat {
int nvcpu; /* number of vcpu */
DirtyRateVcpu *rates; /* array of dirty rate for each vcpu */
} VcpuStat;
/*
* Store calculation statistics for each measure.
*/
+136 -16
View File
@@ -48,6 +48,7 @@
#include "trace.h"
#include "exec/target_page.h"
#include "io/channel-buffer.h"
#include "io/channel-tls.h"
#include "migration/colo.h"
#include "hw/boards.h"
#include "hw/qdev-properties.h"
@@ -215,9 +216,11 @@ void migration_object_init(void)
current_incoming->postcopy_remote_fds =
g_array_new(FALSE, TRUE, sizeof(struct PostCopyFD));
qemu_mutex_init(&current_incoming->rp_mutex);
qemu_mutex_init(&current_incoming->postcopy_prio_thread_mutex);
qemu_event_init(&current_incoming->main_thread_load_event, false);
qemu_sem_init(&current_incoming->postcopy_pause_sem_dst, 0);
qemu_sem_init(&current_incoming->postcopy_pause_sem_fault, 0);
qemu_sem_init(&current_incoming->postcopy_pause_sem_fast_load, 0);
qemu_mutex_init(&current_incoming->page_request_mutex);
current_incoming->page_requested = g_tree_new(page_request_addr_cmp);
@@ -321,6 +324,12 @@ void migration_incoming_state_destroy(void)
mis->page_requested = NULL;
}
if (mis->postcopy_qemufile_dst) {
migration_ioc_unregister_yank_from_file(mis->postcopy_qemufile_dst);
qemu_fclose(mis->postcopy_qemufile_dst);
mis->postcopy_qemufile_dst = NULL;
}
yank_unregister_instance(MIGRATION_YANK_INSTANCE);
}
@@ -691,9 +700,9 @@ static bool postcopy_try_recover(void)
/*
* Here, we only wake up the main loading thread (while the
* fault thread will still be waiting), so that we can receive
* rest threads will still be waiting), so that we can receive
* commands from source now, and answer it if needed. The
* fault thread will be woken up afterwards until we are sure
* rest threads will be woken up afterwards until we are sure
* that source is ready to reply to page requests.
*/
qemu_sem_post(&mis->postcopy_pause_sem_dst);
@@ -714,15 +723,21 @@ void migration_fd_process_incoming(QEMUFile *f, Error **errp)
migration_incoming_process();
}
static bool migration_needs_multiple_sockets(void)
{
return migrate_use_multifd() || migrate_postcopy_preempt();
}
void migration_ioc_process_incoming(QIOChannel *ioc, Error **errp)
{
MigrationIncomingState *mis = migration_incoming_get_current();
Error *local_err = NULL;
bool start_migration;
QEMUFile *f;
if (!mis->from_src_file) {
/* The first connection (multifd may have multiple) */
QEMUFile *f = qemu_file_new_input(ioc);
f = qemu_file_new_input(ioc);
if (!migration_incoming_setup(f, errp)) {
return;
@@ -730,13 +745,19 @@ void migration_ioc_process_incoming(QIOChannel *ioc, Error **errp)
/*
* Common migration only needs one channel, so we can start
* right now. Multifd needs more than one channel, we wait.
* right now. Some features need more than one channel, we wait.
*/
start_migration = !migrate_use_multifd();
start_migration = !migration_needs_multiple_sockets();
} else {
/* Multiple connections */
assert(migrate_use_multifd());
start_migration = multifd_recv_new_channel(ioc, &local_err);
assert(migration_needs_multiple_sockets());
if (migrate_use_multifd()) {
start_migration = multifd_recv_new_channel(ioc, &local_err);
} else {
assert(migrate_postcopy_preempt());
f = qemu_file_new_input(ioc);
start_migration = postcopy_preempt_new_channel(mis, f);
}
if (local_err) {
error_propagate(errp, local_err);
return;
@@ -761,11 +782,20 @@ void migration_ioc_process_incoming(QIOChannel *ioc, Error **errp)
bool migration_has_all_channels(void)
{
MigrationIncomingState *mis = migration_incoming_get_current();
bool all_channels;
all_channels = multifd_recv_all_channels_created();
if (!mis->from_src_file) {
return false;
}
return all_channels && mis->from_src_file != NULL;
if (migrate_use_multifd()) {
return multifd_recv_all_channels_created();
}
if (migrate_postcopy_preempt()) {
return mis->postcopy_qemufile_dst != NULL;
}
return true;
}
/*
@@ -1027,6 +1057,8 @@ static void populate_ram_info(MigrationInfo *info, MigrationState *s)
info->ram->normal_bytes = ram_counters.normal * page_size;
info->ram->mbps = s->mbps;
info->ram->dirty_sync_count = ram_counters.dirty_sync_count;
info->ram->dirty_sync_missed_zero_copy =
ram_counters.dirty_sync_missed_zero_copy;
info->ram->postcopy_requests = ram_counters.postcopy_requests;
info->ram->page_size = page_size;
info->ram->multifd_bytes = ram_counters.multifd_bytes;
@@ -1274,7 +1306,9 @@ static bool migrate_caps_check(bool *cap_list,
#ifdef CONFIG_LINUX
if (cap_list[MIGRATION_CAPABILITY_ZERO_COPY_SEND] &&
(!cap_list[MIGRATION_CAPABILITY_MULTIFD] ||
migrate_use_compression() ||
cap_list[MIGRATION_CAPABILITY_COMPRESS] ||
cap_list[MIGRATION_CAPABILITY_XBZRLE] ||
migrate_multifd_compression() ||
migrate_use_tls())) {
error_setg(errp,
"Zero copy only available for non-compressed non-TLS multifd migration");
@@ -1297,6 +1331,13 @@ static bool migrate_caps_check(bool *cap_list,
return false;
}
if (cap_list[MIGRATION_CAPABILITY_POSTCOPY_PREEMPT]) {
if (!cap_list[MIGRATION_CAPABILITY_POSTCOPY_RAM]) {
error_setg(errp, "Postcopy preempt requires postcopy-ram");
return false;
}
}
return true;
}
@@ -1511,6 +1552,17 @@ static bool migrate_params_check(MigrationParameters *params, Error **errp)
error_prepend(errp, "Invalid mapping given for block-bitmap-mapping: ");
return false;
}
#ifdef CONFIG_LINUX
if (migrate_use_zero_copy_send() &&
((params->has_multifd_compression && params->multifd_compression) ||
(params->has_tls_creds && params->tls_creds && *params->tls_creds))) {
error_setg(errp,
"Zero copy only available for non-compressed non-TLS multifd migration");
return false;
}
#endif
return true;
}
@@ -1867,6 +1919,12 @@ static void migrate_fd_cleanup(MigrationState *s)
qemu_fclose(tmp);
}
if (s->postcopy_qemufile_src) {
migration_ioc_unregister_yank_from_file(s->postcopy_qemufile_src);
qemu_fclose(s->postcopy_qemufile_src);
s->postcopy_qemufile_src = NULL;
}
assert(!migration_is_active(s));
if (s->state == MIGRATION_STATUS_CANCELLING) {
@@ -2663,6 +2721,15 @@ bool migrate_background_snapshot(void)
return s->enabled_capabilities[MIGRATION_CAPABILITY_BACKGROUND_SNAPSHOT];
}
bool migrate_postcopy_preempt(void)
{
MigrationState *s;
s = migrate_get_current();
return s->enabled_capabilities[MIGRATION_CAPABILITY_POSTCOPY_PREEMPT];
}
/* migration thread support */
/*
* Something bad happened to the RP stream, mark an error
@@ -3002,6 +3069,12 @@ static int postcopy_start(MigrationState *ms)
int64_t bandwidth = migrate_max_postcopy_bandwidth();
bool restart_block = false;
int cur_state = MIGRATION_STATUS_ACTIVE;
if (postcopy_preempt_wait_channel(ms)) {
migrate_set_state(&ms->state, ms->state, MIGRATION_STATUS_FAILED);
return -1;
}
if (!migrate_pause_before_switchover()) {
migrate_set_state(&ms->state, MIGRATION_STATUS_ACTIVE,
MIGRATION_STATUS_POSTCOPY_ACTIVE);
@@ -3141,6 +3214,8 @@ static int postcopy_start(MigrationState *ms)
MIGRATION_STATUS_FAILED);
}
trace_postcopy_preempt_enabled(migrate_postcopy_preempt());
return ret;
fail_closefb:
@@ -3253,6 +3328,11 @@ static void migration_completion(MigrationState *s)
qemu_savevm_state_complete_postcopy(s->to_dst_file);
qemu_mutex_unlock_iothread();
/* Shutdown the postcopy fast path thread */
if (migrate_postcopy_preempt()) {
postcopy_preempt_shutdown_file(s);
}
trace_migration_completion_postcopy_end_after_complete();
} else {
goto fail;
@@ -3447,6 +3527,18 @@ static MigThrError postcopy_pause(MigrationState *s)
qemu_file_shutdown(file);
qemu_fclose(file);
/*
* Do the same to postcopy fast path socket too if there is. No
* locking needed because no racer as long as we do this before setting
* status to paused.
*/
if (s->postcopy_qemufile_src) {
migration_ioc_unregister_yank_from_file(s->postcopy_qemufile_src);
qemu_file_shutdown(s->postcopy_qemufile_src);
qemu_fclose(s->postcopy_qemufile_src);
s->postcopy_qemufile_src = NULL;
}
migrate_set_state(&s->state, s->state,
MIGRATION_STATUS_POSTCOPY_PAUSED);
@@ -3464,6 +3556,14 @@ static MigThrError postcopy_pause(MigrationState *s)
if (s->state == MIGRATION_STATUS_POSTCOPY_RECOVER) {
/* Woken up by a recover procedure. Give it a shot */
if (postcopy_preempt_wait_channel(s)) {
/*
* Preempt enabled, and new channel create failed; loop
* back to wait for another recovery.
*/
continue;
}
/*
* Firstly, let's wake up the return path now, with a new
* return path channel.
@@ -3502,8 +3602,13 @@ static MigThrError migration_detect_error(MigrationState *s)
return MIG_THR_ERR_FATAL;
}
/* Try to detect any file errors */
ret = qemu_file_get_error_obj(s->to_dst_file, &local_error);
/*
* Try to detect any file errors. Note that postcopy_qemufile_src will
* be NULL when postcopy preempt is not enabled.
*/
ret = qemu_file_get_error_obj_any(s->to_dst_file,
s->postcopy_qemufile_src,
&local_error);
if (!ret) {
/* Everything is fine */
assert(!local_error);
@@ -4141,6 +4246,15 @@ void migrate_fd_connect(MigrationState *s, Error *error_in)
}
}
/* This needs to be done before resuming a postcopy */
if (postcopy_preempt_setup(s, &local_err)) {
error_report_err(local_err);
migrate_set_state(&s->state, MIGRATION_STATUS_SETUP,
MIGRATION_STATUS_FAILED);
migrate_fd_cleanup(s);
return;
}
if (resume) {
/* Wakeup the main migration thread to do the recovery */
migrate_set_state(&s->state, MIGRATION_STATUS_POSTCOPY_PAUSED,
@@ -4265,6 +4379,11 @@ static Property migration_properties[] = {
DEFINE_PROP_SIZE("announce-step", MigrationState,
parameters.announce_step,
DEFAULT_MIGRATE_ANNOUNCE_STEP),
DEFINE_PROP_BOOL("x-postcopy-preempt-break-huge", MigrationState,
postcopy_preempt_break_huge, true),
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),
/* Migration capabilities */
DEFINE_PROP_MIG_CAP("x-xbzrle", MIGRATION_CAPABILITY_XBZRLE),
@@ -4274,6 +4393,8 @@ static Property migration_properties[] = {
DEFINE_PROP_MIG_CAP("x-compress", MIGRATION_CAPABILITY_COMPRESS),
DEFINE_PROP_MIG_CAP("x-events", MIGRATION_CAPABILITY_EVENTS),
DEFINE_PROP_MIG_CAP("x-postcopy-ram", MIGRATION_CAPABILITY_POSTCOPY_RAM),
DEFINE_PROP_MIG_CAP("x-postcopy-preempt",
MIGRATION_CAPABILITY_POSTCOPY_PREEMPT),
DEFINE_PROP_MIG_CAP("x-colo", MIGRATION_CAPABILITY_X_COLO),
DEFINE_PROP_MIG_CAP("x-release-ram", MIGRATION_CAPABILITY_RELEASE_RAM),
DEFINE_PROP_MIG_CAP("x-block", MIGRATION_CAPABILITY_BLOCK),
@@ -4300,18 +4421,16 @@ static void migration_class_init(ObjectClass *klass, void *data)
static void migration_instance_finalize(Object *obj)
{
MigrationState *ms = MIGRATION_OBJ(obj);
MigrationParameters *params = &ms->parameters;
qemu_mutex_destroy(&ms->error_mutex);
qemu_mutex_destroy(&ms->qemu_file_lock);
g_free(params->tls_hostname);
g_free(params->tls_creds);
qemu_sem_destroy(&ms->wait_unplug_sem);
qemu_sem_destroy(&ms->rate_limit_sem);
qemu_sem_destroy(&ms->pause_sem);
qemu_sem_destroy(&ms->postcopy_pause_sem);
qemu_sem_destroy(&ms->postcopy_pause_rp_sem);
qemu_sem_destroy(&ms->rp_state.rp_sem);
qemu_sem_destroy(&ms->postcopy_qemufile_src_sem);
error_free(ms->error);
}
@@ -4358,6 +4477,7 @@ static void migration_instance_init(Object *obj)
qemu_sem_init(&ms->rp_state.rp_sem, 0);
qemu_sem_init(&ms->rate_limit_sem, 0);
qemu_sem_init(&ms->wait_unplug_sem, 0);
qemu_sem_init(&ms->postcopy_qemufile_src_sem, 0);
qemu_mutex_init(&ms->qemu_file_lock);
}
+43 -1
View File
@@ -23,6 +23,7 @@
#include "io/channel-buffer.h"
#include "net/announce.h"
#include "qom/object.h"
#include "postcopy-ram.h"
struct PostcopyBlocktimeContext;
@@ -67,7 +68,7 @@ typedef struct {
struct MigrationIncomingState {
QEMUFile *from_src_file;
/* Previously received RAM's RAMBlock pointer */
RAMBlock *last_recv_block;
RAMBlock *last_recv_block[RAM_CHANNEL_MAX];
/* A hook to allow cleanup at the end of incoming migration */
void *transport_data;
void (*transport_cleanup)(void *data);
@@ -112,6 +113,23 @@ struct MigrationIncomingState {
* enabled.
*/
unsigned int postcopy_channels;
/* QEMUFile for postcopy only; it'll be handled by a separate thread */
QEMUFile *postcopy_qemufile_dst;
/* Postcopy priority thread is used to receive postcopy requested pages */
QemuThread postcopy_prio_thread;
bool postcopy_prio_thread_created;
/*
* Used to sync between the ram load main thread and the fast ram load
* thread. It protects postcopy_qemufile_dst, which is the postcopy
* fast channel.
*
* The ram fast load thread will take it mostly for the whole lifecycle
* because it needs to continuously read data from the channel, and
* it'll only release this mutex if postcopy is interrupted, so that
* the ram load main thread will take this mutex over and properly
* release the broken channel.
*/
QemuMutex postcopy_prio_thread_mutex;
/*
* An array of temp host huge pages to be used, one for each postcopy
* channel.
@@ -141,6 +159,13 @@ struct MigrationIncomingState {
/* notify PAUSED postcopy incoming migrations to try to continue */
QemuSemaphore postcopy_pause_sem_dst;
QemuSemaphore postcopy_pause_sem_fault;
/*
* This semaphore is used to allow the ram fast load thread (only when
* postcopy preempt is enabled) fall into sleep when there's network
* interruption detected. When the recovery is done, the main load
* thread will kick the fast ram load thread using this semaphore.
*/
QemuSemaphore postcopy_pause_sem_fast_load;
/* List of listening socket addresses */
SocketAddressList *socket_address_list;
@@ -192,6 +217,15 @@ struct MigrationState {
QEMUBH *cleanup_bh;
/* Protected by qemu_file_lock */
QEMUFile *to_dst_file;
/* Postcopy specific transfer channel */
QEMUFile *postcopy_qemufile_src;
/*
* It is posted when the preempt channel is established. Note: this is
* used for both the start or recover of a postcopy migration. We'll
* post to this sem every time a new preempt channel is created in the
* main thread, and we keep post() and wait() in pair.
*/
QemuSemaphore postcopy_qemufile_src_sem;
QIOChannelBuffer *bioc;
/*
* Protects to_dst_file/from_dst_file pointers. We need to make sure we
@@ -306,6 +340,13 @@ struct MigrationState {
bool send_configuration;
/* Whether we send section footer during migration */
bool send_section_footer;
/*
* Whether we allow break sending huge pages when postcopy preempt is
* enabled. When disabled, we won't interrupt precopy within sending a
* host huge page, which is the old behavior of vanilla postcopy.
* NOTE: this parameter is ignored if postcopy preempt is not enabled.
*/
bool postcopy_preempt_break_huge;
/* Needed by postcopy-pause state */
QemuSemaphore postcopy_pause_sem;
@@ -400,6 +441,7 @@ int migrate_decompress_threads(void);
bool migrate_use_events(void);
bool migrate_postcopy_blocktime(void);
bool migrate_background_snapshot(void);
bool migrate_postcopy_preempt(void);
/* Sending on the return path - generic and then for each message type */
void migrate_send_rp_shut(MigrationIncomingState *mis,

Some files were not shown because too many files have changed in this diff Show More