123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935 |
- /*
- * Dirtyrate implement code
- *
- * Copyright (c) 2020 HUAWEI TECHNOLOGIES CO.,LTD.
- *
- * Authors:
- * Chuan Zheng <zhengchuan@huawei.com>
- *
- * 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 "qemu/osdep.h"
- #include "qemu/error-report.h"
- #include "hw/core/cpu.h"
- #include "qapi/error.h"
- #include "exec/ramblock.h"
- #include "exec/target_page.h"
- #include "qemu/rcu_queue.h"
- #include "qemu/main-loop.h"
- #include "qapi/qapi-commands-migration.h"
- #include "ram.h"
- #include "trace.h"
- #include "dirtyrate.h"
- #include "monitor/hmp.h"
- #include "monitor/monitor.h"
- #include "qobject/qdict.h"
- #include "system/kvm.h"
- #include "system/runstate.h"
- #include "exec/memory.h"
- #include "qemu/xxhash.h"
- #include "migration.h"
- /*
- * total_dirty_pages is procted by BQL and is used
- * to stat dirty pages during the period of two
- * memory_global_dirty_log_sync
- */
- uint64_t total_dirty_pages;
- typedef struct DirtyPageRecord {
- uint64_t start_pages;
- uint64_t end_pages;
- } DirtyPageRecord;
- static int CalculatingState = DIRTY_RATE_STATUS_UNSTARTED;
- static struct DirtyRateStat DirtyStat;
- static DirtyRateMeasureMode dirtyrate_mode =
- DIRTY_RATE_MEASURE_MODE_PAGE_SAMPLING;
- static int64_t dirty_stat_wait(int64_t msec, int64_t initial_time)
- {
- int64_t current_time;
- current_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
- if ((current_time - initial_time) >= msec) {
- msec = current_time - initial_time;
- } else {
- g_usleep((msec + initial_time - current_time) * 1000);
- /* g_usleep may overshoot */
- msec = qemu_clock_get_ms(QEMU_CLOCK_REALTIME) - 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 increased_dirty_pages =
- dirty_pages.end_pages - dirty_pages.start_pages;
- /*
- * multiply by 1000ms/s _before_ converting down to megabytes
- * to avoid losing precision
- */
- return qemu_target_pages_to_MiB(increased_dirty_pages * 1000) /
- calc_time_ms;
- }
- void global_dirty_log_change(unsigned int flag, bool start)
- {
- Error *local_err = NULL;
- bool ret;
- bql_lock();
- if (start) {
- ret = memory_global_dirty_log_start(flag, &local_err);
- if (!ret) {
- error_report_err(local_err);
- }
- } else {
- memory_global_dirty_log_stop(flag);
- }
- bql_unlock();
- }
- /*
- * 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)
- {
- bql_lock();
- memory_global_dirty_log_sync(false);
- if (one_shot) {
- memory_global_dirty_log_stop(flag);
- }
- bql_unlock();
- }
- static DirtyPageRecord *vcpu_dirty_stat_alloc(VcpuStat *stat)
- {
- CPUState *cpu;
- int nvcpu = 0;
- CPU_FOREACH(cpu) {
- nvcpu++;
- }
- stat->nvcpu = nvcpu;
- stat->rates = g_new0(DirtyRateVcpu, nvcpu);
- return g_new0(DirtyPageRecord, nvcpu);
- }
- static void vcpu_dirty_stat_collect(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 = NULL;
- int64_t init_time_ms;
- int64_t duration;
- int64_t dirtyrate;
- int i = 0;
- unsigned int gen_id = 0;
- retry:
- init_time_ms = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
- WITH_QEMU_LOCK_GUARD(&qemu_cpu_list_lock) {
- gen_id = cpu_list_generation_id_get();
- records = vcpu_dirty_stat_alloc(stat);
- vcpu_dirty_stat_collect(records, true);
- }
- duration = dirty_stat_wait(calc_time_ms, init_time_ms);
- global_dirty_log_sync(flag, one_shot);
- WITH_QEMU_LOCK_GUARD(&qemu_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(records, false);
- }
- 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_calc_time_valid(int64_t msec)
- {
- if ((msec < MIN_CALC_TIME_MS) || (msec > MAX_CALC_TIME_MS)) {
- return false;
- }
- return true;
- }
- static bool is_sample_pages_valid(int64_t pages)
- {
- return pages >= MIN_SAMPLE_PAGE_COUNT &&
- pages <= MAX_SAMPLE_PAGE_COUNT;
- }
- static int dirtyrate_set_state(int *state, int old_state, int new_state)
- {
- assert(new_state < DIRTY_RATE_STATUS__MAX);
- trace_dirtyrate_set_state(DirtyRateStatus_str(new_state));
- if (qatomic_cmpxchg(state, old_state, new_state) == old_state) {
- return 0;
- } else {
- return -1;
- }
- }
- /* Decimal power of given time unit relative to one second */
- static int time_unit_to_power(TimeUnit time_unit)
- {
- switch (time_unit) {
- case TIME_UNIT_SECOND:
- return 0;
- case TIME_UNIT_MILLISECOND:
- return -3;
- default:
- g_assert_not_reached();
- }
- }
- static int64_t convert_time_unit(int64_t value, TimeUnit unit_from,
- TimeUnit unit_to)
- {
- int power = time_unit_to_power(unit_from) -
- time_unit_to_power(unit_to);
- while (power < 0) {
- value /= 10;
- power += 1;
- }
- while (power > 0) {
- value *= 10;
- power -= 1;
- }
- return value;
- }
- static struct DirtyRateInfo *
- query_dirty_rate_info(TimeUnit calc_time_unit)
- {
- int i;
- int64_t dirty_rate = DirtyStat.dirty_rate;
- struct DirtyRateInfo *info = g_new0(DirtyRateInfo, 1);
- DirtyRateVcpuList *head = NULL, **tail = &head;
- info->status = CalculatingState;
- info->start_time = DirtyStat.start_time;
- info->calc_time = convert_time_unit(DirtyStat.calc_time_ms,
- TIME_UNIT_MILLISECOND,
- calc_time_unit);
- info->calc_time_unit = calc_time_unit;
- info->sample_pages = DirtyStat.sample_pages;
- info->mode = dirtyrate_mode;
- if (qatomic_read(&CalculatingState) == DIRTY_RATE_STATUS_MEASURED) {
- info->has_dirty_rate = true;
- info->dirty_rate = dirty_rate;
- if (dirtyrate_mode == DIRTY_RATE_MEASURE_MODE_DIRTY_RING) {
- /*
- * set sample_pages with 0 to indicate page sampling
- * isn't enabled
- **/
- info->sample_pages = 0;
- info->has_vcpu_dirty_rate = true;
- for (i = 0; i < DirtyStat.dirty_ring.nvcpu; i++) {
- DirtyRateVcpu *rate = g_new0(DirtyRateVcpu, 1);
- rate->id = DirtyStat.dirty_ring.rates[i].id;
- rate->dirty_rate = DirtyStat.dirty_ring.rates[i].dirty_rate;
- QAPI_LIST_APPEND(tail, rate);
- }
- info->vcpu_dirty_rate = head;
- }
- if (dirtyrate_mode == DIRTY_RATE_MEASURE_MODE_DIRTY_BITMAP) {
- info->sample_pages = 0;
- }
- }
- trace_query_dirty_rate_info(DirtyRateStatus_str(CalculatingState));
- return info;
- }
- static void init_dirtyrate_stat(struct DirtyRateConfig config)
- {
- DirtyStat.dirty_rate = -1;
- DirtyStat.start_time = qemu_clock_get_ms(QEMU_CLOCK_HOST) / 1000;
- DirtyStat.calc_time_ms = config.calc_time_ms;
- DirtyStat.sample_pages = config.sample_pages_per_gigabytes;
- switch (config.mode) {
- case DIRTY_RATE_MEASURE_MODE_PAGE_SAMPLING:
- DirtyStat.page_sampling.total_dirty_samples = 0;
- DirtyStat.page_sampling.total_sample_count = 0;
- DirtyStat.page_sampling.total_block_mem_MB = 0;
- break;
- case DIRTY_RATE_MEASURE_MODE_DIRTY_RING:
- DirtyStat.dirty_ring.nvcpu = -1;
- DirtyStat.dirty_ring.rates = NULL;
- break;
- default:
- break;
- }
- }
- static void cleanup_dirtyrate_stat(struct DirtyRateConfig config)
- {
- /* last calc-dirty-rate qmp use dirty ring mode */
- if (dirtyrate_mode == DIRTY_RATE_MEASURE_MODE_DIRTY_RING) {
- free(DirtyStat.dirty_ring.rates);
- DirtyStat.dirty_ring.rates = NULL;
- }
- }
- static void update_dirtyrate_stat(struct RamblockDirtyInfo *info)
- {
- DirtyStat.page_sampling.total_dirty_samples += info->sample_dirty_count;
- DirtyStat.page_sampling.total_sample_count += info->sample_pages_count;
- /* size of total pages in MB */
- DirtyStat.page_sampling.total_block_mem_MB +=
- qemu_target_pages_to_MiB(info->ramblock_pages);
- }
- static void update_dirtyrate(uint64_t msec)
- {
- uint64_t dirtyrate;
- uint64_t total_dirty_samples = DirtyStat.page_sampling.total_dirty_samples;
- uint64_t total_sample_count = DirtyStat.page_sampling.total_sample_count;
- uint64_t total_block_mem_MB = DirtyStat.page_sampling.total_block_mem_MB;
- dirtyrate = total_dirty_samples * total_block_mem_MB *
- 1000 / (total_sample_count * msec);
- DirtyStat.dirty_rate = dirtyrate;
- }
- /*
- * Compute hash of a single page of size TARGET_PAGE_SIZE.
- */
- static uint32_t compute_page_hash(void *ptr)
- {
- size_t page_size = qemu_target_page_size();
- uint32_t i;
- uint64_t v1, v2, v3, v4;
- uint64_t res;
- const uint64_t *p = ptr;
- v1 = QEMU_XXHASH_SEED + XXH_PRIME64_1 + XXH_PRIME64_2;
- v2 = QEMU_XXHASH_SEED + XXH_PRIME64_2;
- v3 = QEMU_XXHASH_SEED + 0;
- v4 = QEMU_XXHASH_SEED - XXH_PRIME64_1;
- for (i = 0; i < page_size / 8; i += 4) {
- v1 = XXH64_round(v1, p[i + 0]);
- v2 = XXH64_round(v2, p[i + 1]);
- v3 = XXH64_round(v3, p[i + 2]);
- v4 = XXH64_round(v4, p[i + 3]);
- }
- res = XXH64_mergerounds(v1, v2, v3, v4);
- res += page_size;
- res = XXH64_avalanche(res);
- return (uint32_t)(res & UINT32_MAX);
- }
- /*
- * get hash result for the sampled memory with length of TARGET_PAGE_SIZE
- * in ramblock, which starts from ramblock base address.
- */
- static uint32_t get_ramblock_vfn_hash(struct RamblockDirtyInfo *info,
- uint64_t vfn)
- {
- uint32_t hash;
- hash = compute_page_hash(info->ramblock_addr +
- vfn * qemu_target_page_size());
- trace_get_ramblock_vfn_hash(info->idstr, vfn, hash);
- return hash;
- }
- static bool save_ramblock_hash(struct RamblockDirtyInfo *info)
- {
- unsigned int sample_pages_count;
- int i;
- GRand *rand;
- sample_pages_count = info->sample_pages_count;
- /* ramblock size less than one page, return success to skip this ramblock */
- if (unlikely(info->ramblock_pages == 0 || sample_pages_count == 0)) {
- return true;
- }
- info->hash_result = g_try_malloc0_n(sample_pages_count,
- sizeof(uint32_t));
- if (!info->hash_result) {
- return false;
- }
- info->sample_page_vfn = g_try_malloc0_n(sample_pages_count,
- sizeof(uint64_t));
- if (!info->sample_page_vfn) {
- g_free(info->hash_result);
- return false;
- }
- rand = g_rand_new();
- for (i = 0; i < sample_pages_count; i++) {
- info->sample_page_vfn[i] = g_rand_int_range(rand, 0,
- info->ramblock_pages - 1);
- info->hash_result[i] = get_ramblock_vfn_hash(info,
- info->sample_page_vfn[i]);
- }
- g_rand_free(rand);
- return true;
- }
- static void get_ramblock_dirty_info(RAMBlock *block,
- struct RamblockDirtyInfo *info,
- 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) *
- sample_pages_per_gigabytes) >> 30;
- /* Right shift TARGET_PAGE_BITS to calc page count */
- info->ramblock_pages = qemu_ram_get_used_length(block) >>
- qemu_target_page_bits();
- info->ramblock_addr = qemu_ram_get_host_addr(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)
- {
- int i;
- if (!infos) {
- return;
- }
- for (i = 0; i < count; i++) {
- g_free(infos[i].sample_page_vfn);
- g_free(infos[i].hash_result);
- }
- g_free(infos);
- }
- static bool skip_sample_ramblock(RAMBlock *block)
- {
- /*
- * Sample only blocks larger than MIN_RAMBLOCK_SIZE.
- */
- if (qemu_ram_get_used_length(block) < (MIN_RAMBLOCK_SIZE << 10)) {
- trace_skip_sample_ramblock(block->idstr,
- qemu_ram_get_used_length(block));
- return true;
- }
- return false;
- }
- static bool record_ramblock_hash_info(struct RamblockDirtyInfo **block_dinfo,
- struct DirtyRateConfig config,
- int *block_count)
- {
- struct RamblockDirtyInfo *info = NULL;
- struct RamblockDirtyInfo *dinfo = NULL;
- RAMBlock *block = NULL;
- int total_count = 0;
- int index = 0;
- bool ret = false;
- RAMBLOCK_FOREACH_MIGRATABLE(block) {
- if (skip_sample_ramblock(block)) {
- continue;
- }
- total_count++;
- }
- dinfo = g_try_malloc0_n(total_count, sizeof(struct RamblockDirtyInfo));
- if (dinfo == NULL) {
- goto out;
- }
- RAMBLOCK_FOREACH_MIGRATABLE(block) {
- if (skip_sample_ramblock(block)) {
- continue;
- }
- if (index >= total_count) {
- break;
- }
- info = &dinfo[index];
- get_ramblock_dirty_info(block, info, &config);
- if (!save_ramblock_hash(info)) {
- goto out;
- }
- index++;
- }
- ret = true;
- out:
- *block_count = index;
- *block_dinfo = dinfo;
- return ret;
- }
- static void calc_page_dirty_rate(struct RamblockDirtyInfo *info)
- {
- uint32_t hash;
- int i;
- for (i = 0; i < info->sample_pages_count; i++) {
- hash = get_ramblock_vfn_hash(info, info->sample_page_vfn[i]);
- if (hash != info->hash_result[i]) {
- trace_calc_page_dirty_rate(info->idstr, hash, info->hash_result[i]);
- info->sample_dirty_count++;
- }
- }
- }
- static struct RamblockDirtyInfo *
- find_block_matched(RAMBlock *block, int count,
- struct RamblockDirtyInfo *infos)
- {
- int i;
- for (i = 0; i < count; i++) {
- if (!strcmp(infos[i].idstr, qemu_ram_get_idstr(block))) {
- break;
- }
- }
- if (i == count) {
- return NULL;
- }
- if (infos[i].ramblock_addr != qemu_ram_get_host_addr(block) ||
- infos[i].ramblock_pages !=
- (qemu_ram_get_used_length(block) >> qemu_target_page_bits())) {
- trace_find_page_matched(block->idstr);
- return NULL;
- }
- return &infos[i];
- }
- static bool compare_page_hash_info(struct RamblockDirtyInfo *info,
- int block_count)
- {
- struct RamblockDirtyInfo *block_dinfo = NULL;
- RAMBlock *block = NULL;
- RAMBLOCK_FOREACH_MIGRATABLE(block) {
- if (skip_sample_ramblock(block)) {
- continue;
- }
- block_dinfo = find_block_matched(block, block_count, info);
- if (block_dinfo == NULL) {
- continue;
- }
- calc_page_dirty_rate(block_dinfo);
- update_dirtyrate_stat(block_dinfo);
- }
- if (DirtyStat.page_sampling.total_sample_count == 0) {
- return false;
- }
- return true;
- }
- static inline void record_dirtypages_bitmap(DirtyPageRecord *dirty_pages,
- bool start)
- {
- if (start) {
- dirty_pages->start_pages = total_dirty_pages;
- } else {
- dirty_pages->end_pages = total_dirty_pages;
- }
- }
- static inline void dirtyrate_manual_reset_protect(void)
- {
- RAMBlock *block = NULL;
- WITH_RCU_READ_LOCK_GUARD() {
- RAMBLOCK_FOREACH_MIGRATABLE(block) {
- memory_region_clear_dirty_bitmap(block->mr, 0,
- block->used_length);
- }
- }
- }
- static void calculate_dirtyrate_dirty_bitmap(struct DirtyRateConfig config)
- {
- int64_t start_time;
- DirtyPageRecord dirty_pages;
- Error *local_err = NULL;
- bql_lock();
- if (!memory_global_dirty_log_start(GLOBAL_DIRTY_DIRTY_RATE, &local_err)) {
- error_report_err(local_err);
- }
- /*
- * 1'round of log sync may return all 1 bits with
- * KVM_DIRTY_LOG_INITIALLY_SET enable
- * skip it unconditionally and start dirty tracking
- * from 2'round of log sync
- */
- memory_global_dirty_log_sync(false);
- /*
- * reset page protect manually and unconditionally.
- * this make sure kvm dirty log be cleared if
- * KVM_DIRTY_LOG_MANUAL_PROTECT_ENABLE cap is enabled.
- */
- dirtyrate_manual_reset_protect();
- bql_unlock();
- record_dirtypages_bitmap(&dirty_pages, true);
- start_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
- DirtyStat.start_time = qemu_clock_get_ms(QEMU_CLOCK_HOST) / 1000;
- DirtyStat.calc_time_ms = dirty_stat_wait(config.calc_time_ms, start_time);
- /*
- * do two things.
- * 1. fetch dirty bitmap from kvm
- * 2. stop dirty tracking
- */
- global_dirty_log_sync(GLOBAL_DIRTY_DIRTY_RATE, true);
- record_dirtypages_bitmap(&dirty_pages, false);
- DirtyStat.dirty_rate = do_calculate_dirtyrate(dirty_pages,
- DirtyStat.calc_time_ms);
- }
- static void calculate_dirtyrate_dirty_ring(struct DirtyRateConfig config)
- {
- uint64_t dirtyrate = 0;
- uint64_t dirtyrate_sum = 0;
- int i = 0;
- /* start log sync */
- global_dirty_log_change(GLOBAL_DIRTY_DIRTY_RATE, true);
- DirtyStat.start_time = qemu_clock_get_ms(QEMU_CLOCK_HOST) / 1000;
- /* calculate vcpu dirtyrate */
- DirtyStat.calc_time_ms = vcpu_calculate_dirtyrate(config.calc_time_ms,
- &DirtyStat.dirty_ring,
- GLOBAL_DIRTY_DIRTY_RATE,
- true);
- /* calculate vm dirtyrate */
- for (i = 0; i < DirtyStat.dirty_ring.nvcpu; 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;
- }
- static void calculate_dirtyrate_sample_vm(struct DirtyRateConfig config)
- {
- struct RamblockDirtyInfo *block_dinfo = NULL;
- int block_count = 0;
- int64_t initial_time;
- rcu_read_lock();
- initial_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
- DirtyStat.start_time = qemu_clock_get_ms(QEMU_CLOCK_HOST) / 1000;
- if (!record_ramblock_hash_info(&block_dinfo, config, &block_count)) {
- goto out;
- }
- rcu_read_unlock();
- DirtyStat.calc_time_ms = dirty_stat_wait(config.calc_time_ms,
- initial_time);
- rcu_read_lock();
- if (!compare_page_hash_info(block_dinfo, block_count)) {
- goto out;
- }
- update_dirtyrate(DirtyStat.calc_time_ms);
- out:
- rcu_read_unlock();
- free_ramblock_dirty_info(block_dinfo, block_count);
- }
- static void calculate_dirtyrate(struct DirtyRateConfig config)
- {
- if (config.mode == DIRTY_RATE_MEASURE_MODE_DIRTY_BITMAP) {
- calculate_dirtyrate_dirty_bitmap(config);
- } else if (config.mode == DIRTY_RATE_MEASURE_MODE_DIRTY_RING) {
- calculate_dirtyrate_dirty_ring(config);
- } else {
- calculate_dirtyrate_sample_vm(config);
- }
- trace_dirtyrate_calculate(DirtyStat.dirty_rate);
- }
- void *get_dirtyrate_thread(void *arg)
- {
- struct DirtyRateConfig config = *(struct DirtyRateConfig *)arg;
- int ret;
- rcu_register_thread();
- ret = dirtyrate_set_state(&CalculatingState, DIRTY_RATE_STATUS_UNSTARTED,
- DIRTY_RATE_STATUS_MEASURING);
- if (ret == -1) {
- error_report("change dirtyrate state failed.");
- return NULL;
- }
- calculate_dirtyrate(config);
- ret = dirtyrate_set_state(&CalculatingState, DIRTY_RATE_STATUS_MEASURING,
- DIRTY_RATE_STATUS_MEASURED);
- if (ret == -1) {
- error_report("change dirtyrate state failed.");
- }
- rcu_unregister_thread();
- return NULL;
- }
- void qmp_calc_dirty_rate(int64_t calc_time,
- bool has_calc_time_unit,
- TimeUnit calc_time_unit,
- bool has_sample_pages,
- int64_t sample_pages,
- bool has_mode,
- DirtyRateMeasureMode mode,
- Error **errp)
- {
- static struct DirtyRateConfig config;
- QemuThread thread;
- int ret;
- /*
- * If the dirty rate is already being measured, don't attempt to start.
- */
- if (qatomic_read(&CalculatingState) == DIRTY_RATE_STATUS_MEASURING) {
- error_setg(errp, "the dirty rate is already being measured.");
- return;
- }
- int64_t calc_time_ms = convert_time_unit(
- calc_time,
- has_calc_time_unit ? calc_time_unit : TIME_UNIT_SECOND,
- TIME_UNIT_MILLISECOND
- );
- if (!is_calc_time_valid(calc_time_ms)) {
- error_setg(errp, "Calculation time is out of range [%dms, %dms].",
- MIN_CALC_TIME_MS, MAX_CALC_TIME_MS);
- return;
- }
- if (!has_mode) {
- mode = DIRTY_RATE_MEASURE_MODE_PAGE_SAMPLING;
- }
- if (has_sample_pages && mode != DIRTY_RATE_MEASURE_MODE_PAGE_SAMPLING) {
- error_setg(errp, "sample-pages is used only in page-sampling mode");
- return;
- }
- if (has_sample_pages) {
- if (!is_sample_pages_valid(sample_pages)) {
- error_setg(errp, "sample-pages is out of range[%d, %d].",
- MIN_SAMPLE_PAGE_COUNT,
- MAX_SAMPLE_PAGE_COUNT);
- return;
- }
- } else {
- sample_pages = DIRTYRATE_DEFAULT_SAMPLE_PAGES;
- }
- /*
- * dirty ring mode only works when kvm dirty ring is enabled.
- * on the contrary, dirty bitmap mode is not.
- */
- if (((mode == DIRTY_RATE_MEASURE_MODE_DIRTY_RING) &&
- !kvm_dirty_ring_enabled()) ||
- ((mode == DIRTY_RATE_MEASURE_MODE_DIRTY_BITMAP) &&
- kvm_dirty_ring_enabled())) {
- error_setg(errp, "mode %s is not enabled, use other method instead.",
- DirtyRateMeasureMode_str(mode));
- return;
- }
- /*
- * Init calculation state as unstarted.
- */
- ret = dirtyrate_set_state(&CalculatingState, CalculatingState,
- DIRTY_RATE_STATUS_UNSTARTED);
- if (ret == -1) {
- error_setg(errp, "init dirty rate calculation state failed.");
- return;
- }
- config.calc_time_ms = calc_time_ms;
- config.sample_pages_per_gigabytes = sample_pages;
- config.mode = mode;
- cleanup_dirtyrate_stat(config);
- /*
- * update dirty rate mode so that we can figure out what mode has
- * been used in last calculation
- **/
- dirtyrate_mode = mode;
- init_dirtyrate_stat(config);
- qemu_thread_create(&thread, MIGRATION_THREAD_DIRTY_RATE,
- get_dirtyrate_thread, (void *)&config,
- QEMU_THREAD_DETACHED);
- }
- struct DirtyRateInfo *qmp_query_dirty_rate(bool has_calc_time_unit,
- TimeUnit calc_time_unit,
- Error **errp)
- {
- return query_dirty_rate_info(
- has_calc_time_unit ? calc_time_unit : TIME_UNIT_SECOND);
- }
- void hmp_info_dirty_rate(Monitor *mon, const QDict *qdict)
- {
- DirtyRateInfo *info = query_dirty_rate_info(TIME_UNIT_SECOND);
- monitor_printf(mon, "Status: %s\n",
- DirtyRateStatus_str(info->status));
- monitor_printf(mon, "Start Time: %"PRIi64" (ms)\n",
- info->start_time);
- if (info->mode == DIRTY_RATE_MEASURE_MODE_PAGE_SAMPLING) {
- monitor_printf(mon, "Sample Pages: %"PRIu64" (per GB)\n",
- info->sample_pages);
- }
- monitor_printf(mon, "Period: %"PRIi64" (sec)\n",
- info->calc_time);
- monitor_printf(mon, "Mode: %s\n",
- DirtyRateMeasureMode_str(info->mode));
- monitor_printf(mon, "Dirty rate: ");
- if (info->has_dirty_rate) {
- monitor_printf(mon, "%"PRIi64" (MB/s)\n", info->dirty_rate);
- if (info->has_vcpu_dirty_rate) {
- DirtyRateVcpuList *rate, *head = info->vcpu_dirty_rate;
- for (rate = head; rate != NULL; rate = rate->next) {
- monitor_printf(mon, "vcpu[%"PRIi64"], Dirty rate: %"PRIi64
- " (MB/s)\n", rate->value->id,
- rate->value->dirty_rate);
- }
- }
- } else {
- monitor_printf(mon, "(not ready)\n");
- }
- qapi_free_DirtyRateVcpuList(info->vcpu_dirty_rate);
- g_free(info);
- }
- void hmp_calc_dirty_rate(Monitor *mon, const QDict *qdict)
- {
- int64_t sec = qdict_get_try_int(qdict, "second", 0);
- int64_t sample_pages = qdict_get_try_int(qdict, "sample_pages_per_GB", -1);
- bool has_sample_pages = (sample_pages != -1);
- bool dirty_ring = qdict_get_try_bool(qdict, "dirty_ring", false);
- bool dirty_bitmap = qdict_get_try_bool(qdict, "dirty_bitmap", false);
- DirtyRateMeasureMode mode = DIRTY_RATE_MEASURE_MODE_PAGE_SAMPLING;
- Error *err = NULL;
- if (!sec) {
- monitor_printf(mon, "Incorrect period length specified!\n");
- return;
- }
- if (dirty_ring && dirty_bitmap) {
- monitor_printf(mon, "Either dirty ring or dirty bitmap "
- "can be specified!\n");
- return;
- }
- if (dirty_bitmap) {
- mode = DIRTY_RATE_MEASURE_MODE_DIRTY_BITMAP;
- } else if (dirty_ring) {
- mode = DIRTY_RATE_MEASURE_MODE_DIRTY_RING;
- }
- qmp_calc_dirty_rate(sec, /* calc-time */
- false, TIME_UNIT_SECOND, /* calc-time-unit */
- has_sample_pages, sample_pages,
- true, mode,
- &err);
- if (err) {
- hmp_handle_error(mon, err);
- return;
- }
- monitor_printf(mon, "Starting dirty rate measurement with period %"PRIi64
- " seconds\n", sec);
- monitor_printf(mon, "[Please use 'info dirty_rate' to check results]\n");
- }
|