14240dceeSChuan Zheng /* 24240dceeSChuan Zheng * Dirtyrate implement code 34240dceeSChuan Zheng * 44240dceeSChuan Zheng * Copyright (c) 2020 HUAWEI TECHNOLOGIES CO.,LTD. 54240dceeSChuan Zheng * 64240dceeSChuan Zheng * Authors: 74240dceeSChuan Zheng * Chuan Zheng <zhengchuan@huawei.com> 84240dceeSChuan Zheng * 94240dceeSChuan Zheng * This work is licensed under the terms of the GNU GPL, version 2 or later. 104240dceeSChuan Zheng * See the COPYING file in the top-level directory. 114240dceeSChuan Zheng */ 124240dceeSChuan Zheng 134240dceeSChuan Zheng #include "qemu/osdep.h" 14cc37d98bSRichard Henderson #include "qemu/error-report.h" 151e05888aSPhilippe Mathieu-Daudé #include "hw/core/cpu.h" 164240dceeSChuan Zheng #include "qapi/error.h" 174240dceeSChuan Zheng #include "exec/ramblock.h" 18beeda9b7SJuan Quintela #include "exec/target_page.h" 194240dceeSChuan Zheng #include "qemu/rcu_queue.h" 200e21bf24SHyman Huang(黄勇) #include "qemu/main-loop.h" 214240dceeSChuan Zheng #include "qapi/qapi-commands-migration.h" 223ded54b1SChuan Zheng #include "ram.h" 233c0b5dffSChuan Zheng #include "trace.h" 244240dceeSChuan Zheng #include "dirtyrate.h" 25a4a571d9SPeter Xu #include "monitor/hmp.h" 26a4a571d9SPeter Xu #include "monitor/monitor.h" 27*407bc4bfSDaniel P. Berrangé #include "qobject/qdict.h" 2832cad1ffSPhilippe Mathieu-Daudé #include "system/kvm.h" 2932cad1ffSPhilippe Mathieu-Daudé #include "system/runstate.h" 300e21bf24SHyman Huang(黄勇) #include "exec/memory.h" 3100a3f9c6SAndrei Gudkov #include "qemu/xxhash.h" 32e620b1e4SPeter Xu #include "migration.h" 330e21bf24SHyman Huang(黄勇) 344998a37eSHyman Huang(黄勇) /* 354998a37eSHyman Huang(黄勇) * total_dirty_pages is procted by BQL and is used 364998a37eSHyman Huang(黄勇) * to stat dirty pages during the period of two 374998a37eSHyman Huang(黄勇) * memory_global_dirty_log_sync 384998a37eSHyman Huang(黄勇) */ 394998a37eSHyman Huang(黄勇) uint64_t total_dirty_pages; 404998a37eSHyman Huang(黄勇) 410e21bf24SHyman Huang(黄勇) typedef struct DirtyPageRecord { 420e21bf24SHyman Huang(黄勇) uint64_t start_pages; 430e21bf24SHyman Huang(黄勇) uint64_t end_pages; 440e21bf24SHyman Huang(黄勇) } DirtyPageRecord; 454240dceeSChuan Zheng 467df3aa30SChuan Zheng static int CalculatingState = DIRTY_RATE_STATUS_UNSTARTED; 47c9a58d71SChuan Zheng static struct DirtyRateStat DirtyStat; 480e21bf24SHyman Huang(黄勇) static DirtyRateMeasureMode dirtyrate_mode = 490e21bf24SHyman Huang(黄勇) DIRTY_RATE_MEASURE_MODE_PAGE_SAMPLING; 507df3aa30SChuan Zheng 518244166dSHyman Huang(黄勇) static int64_t dirty_stat_wait(int64_t msec, int64_t initial_time) 52eca58224SChuan Zheng { 53eca58224SChuan Zheng int64_t current_time; 54eca58224SChuan Zheng 55eca58224SChuan Zheng current_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); 56eca58224SChuan Zheng if ((current_time - initial_time) >= msec) { 57eca58224SChuan Zheng msec = current_time - initial_time; 58eca58224SChuan Zheng } else { 59eca58224SChuan Zheng g_usleep((msec + initial_time - current_time) * 1000); 603eb82637SAndrei Gudkov /* g_usleep may overshoot */ 613eb82637SAndrei Gudkov msec = qemu_clock_get_ms(QEMU_CLOCK_REALTIME) - initial_time; 62eca58224SChuan Zheng } 63eca58224SChuan Zheng 64eca58224SChuan Zheng return msec; 65eca58224SChuan Zheng } 66eca58224SChuan Zheng 678244166dSHyman Huang(黄勇) static inline void record_dirtypages(DirtyPageRecord *dirty_pages, 688244166dSHyman Huang(黄勇) CPUState *cpu, bool start) 698244166dSHyman Huang(黄勇) { 708244166dSHyman Huang(黄勇) if (start) { 718244166dSHyman Huang(黄勇) dirty_pages[cpu->cpu_index].start_pages = cpu->dirty_pages; 728244166dSHyman Huang(黄勇) } else { 738244166dSHyman Huang(黄勇) dirty_pages[cpu->cpu_index].end_pages = cpu->dirty_pages; 748244166dSHyman Huang(黄勇) } 758244166dSHyman Huang(黄勇) } 768244166dSHyman Huang(黄勇) 778244166dSHyman Huang(黄勇) static int64_t do_calculate_dirtyrate(DirtyPageRecord dirty_pages, 788244166dSHyman Huang(黄勇) int64_t calc_time_ms) 798244166dSHyman Huang(黄勇) { 808244166dSHyman Huang(黄勇) uint64_t increased_dirty_pages = 818244166dSHyman Huang(黄勇) dirty_pages.end_pages - dirty_pages.start_pages; 828244166dSHyman Huang(黄勇) 833eb82637SAndrei Gudkov /* 843eb82637SAndrei Gudkov * multiply by 1000ms/s _before_ converting down to megabytes 853eb82637SAndrei Gudkov * to avoid losing precision 863eb82637SAndrei Gudkov */ 873eb82637SAndrei Gudkov return qemu_target_pages_to_MiB(increased_dirty_pages * 1000) / 883eb82637SAndrei Gudkov calc_time_ms; 898244166dSHyman Huang(黄勇) } 908244166dSHyman Huang(黄勇) 918244166dSHyman Huang(黄勇) void global_dirty_log_change(unsigned int flag, bool start) 928244166dSHyman Huang(黄勇) { 93639ec3fbSCédric Le Goater Error *local_err = NULL; 94639ec3fbSCédric Le Goater bool ret; 95639ec3fbSCédric Le Goater 96195801d7SStefan Hajnoczi bql_lock(); 978244166dSHyman Huang(黄勇) if (start) { 98639ec3fbSCédric Le Goater ret = memory_global_dirty_log_start(flag, &local_err); 99639ec3fbSCédric Le Goater if (!ret) { 100639ec3fbSCédric Le Goater error_report_err(local_err); 101639ec3fbSCédric Le Goater } 1028244166dSHyman Huang(黄勇) } else { 1038244166dSHyman Huang(黄勇) memory_global_dirty_log_stop(flag); 1048244166dSHyman Huang(黄勇) } 105195801d7SStefan Hajnoczi bql_unlock(); 1068244166dSHyman Huang(黄勇) } 1078244166dSHyman Huang(黄勇) 1088244166dSHyman Huang(黄勇) /* 1098244166dSHyman Huang(黄勇) * global_dirty_log_sync 1108244166dSHyman Huang(黄勇) * 1. sync dirty log from kvm 1118244166dSHyman Huang(黄勇) * 2. stop dirty tracking if needed. 1128244166dSHyman Huang(黄勇) */ 1138244166dSHyman Huang(黄勇) static void global_dirty_log_sync(unsigned int flag, bool one_shot) 1148244166dSHyman Huang(黄勇) { 115195801d7SStefan Hajnoczi bql_lock(); 1161e493be5SGavin Shan memory_global_dirty_log_sync(false); 1178244166dSHyman Huang(黄勇) if (one_shot) { 1188244166dSHyman Huang(黄勇) memory_global_dirty_log_stop(flag); 1198244166dSHyman Huang(黄勇) } 120195801d7SStefan Hajnoczi bql_unlock(); 1218244166dSHyman Huang(黄勇) } 1228244166dSHyman Huang(黄勇) 1238244166dSHyman Huang(黄勇) static DirtyPageRecord *vcpu_dirty_stat_alloc(VcpuStat *stat) 1248244166dSHyman Huang(黄勇) { 1258244166dSHyman Huang(黄勇) CPUState *cpu; 1268244166dSHyman Huang(黄勇) int nvcpu = 0; 1278244166dSHyman Huang(黄勇) 1288244166dSHyman Huang(黄勇) CPU_FOREACH(cpu) { 1298244166dSHyman Huang(黄勇) nvcpu++; 1308244166dSHyman Huang(黄勇) } 1318244166dSHyman Huang(黄勇) 1328244166dSHyman Huang(黄勇) stat->nvcpu = nvcpu; 133c5e8d518SMarkus Armbruster stat->rates = g_new0(DirtyRateVcpu, nvcpu); 1348244166dSHyman Huang(黄勇) 13566997c42SMarkus Armbruster return g_new0(DirtyPageRecord, nvcpu); 1368244166dSHyman Huang(黄勇) } 1378244166dSHyman Huang(黄勇) 1384918712fSWafer static void vcpu_dirty_stat_collect(DirtyPageRecord *records, 1398244166dSHyman Huang(黄勇) bool start) 1408244166dSHyman Huang(黄勇) { 1418244166dSHyman Huang(黄勇) CPUState *cpu; 1428244166dSHyman Huang(黄勇) 1438244166dSHyman Huang(黄勇) CPU_FOREACH(cpu) { 1448244166dSHyman Huang(黄勇) record_dirtypages(records, cpu, start); 1458244166dSHyman Huang(黄勇) } 1468244166dSHyman Huang(黄勇) } 1478244166dSHyman Huang(黄勇) 1488244166dSHyman Huang(黄勇) int64_t vcpu_calculate_dirtyrate(int64_t calc_time_ms, 1498244166dSHyman Huang(黄勇) VcpuStat *stat, 1508244166dSHyman Huang(黄勇) unsigned int flag, 1518244166dSHyman Huang(黄勇) bool one_shot) 1528244166dSHyman Huang(黄勇) { 1537cea8637SMarc-André Lureau DirtyPageRecord *records = NULL; 1548244166dSHyman Huang(黄勇) int64_t init_time_ms; 1558244166dSHyman Huang(黄勇) int64_t duration; 1568244166dSHyman Huang(黄勇) int64_t dirtyrate; 1578244166dSHyman Huang(黄勇) int i = 0; 1587cea8637SMarc-André Lureau unsigned int gen_id = 0; 1598244166dSHyman Huang(黄勇) 1608244166dSHyman Huang(黄勇) retry: 1618244166dSHyman Huang(黄勇) init_time_ms = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); 1628244166dSHyman Huang(黄勇) 163370ed600SJamie Iles WITH_QEMU_LOCK_GUARD(&qemu_cpu_list_lock) { 1648244166dSHyman Huang(黄勇) gen_id = cpu_list_generation_id_get(); 1658244166dSHyman Huang(黄勇) records = vcpu_dirty_stat_alloc(stat); 1664918712fSWafer vcpu_dirty_stat_collect(records, true); 167370ed600SJamie Iles } 1688244166dSHyman Huang(黄勇) 1698244166dSHyman Huang(黄勇) duration = dirty_stat_wait(calc_time_ms, init_time_ms); 1708244166dSHyman Huang(黄勇) 1718244166dSHyman Huang(黄勇) global_dirty_log_sync(flag, one_shot); 1728244166dSHyman Huang(黄勇) 173370ed600SJamie Iles WITH_QEMU_LOCK_GUARD(&qemu_cpu_list_lock) { 1748244166dSHyman Huang(黄勇) if (gen_id != cpu_list_generation_id_get()) { 1758244166dSHyman Huang(黄勇) g_free(records); 1768244166dSHyman Huang(黄勇) g_free(stat->rates); 1778244166dSHyman Huang(黄勇) cpu_list_unlock(); 1788244166dSHyman Huang(黄勇) goto retry; 1798244166dSHyman Huang(黄勇) } 1804918712fSWafer vcpu_dirty_stat_collect(records, false); 181370ed600SJamie Iles } 1828244166dSHyman Huang(黄勇) 1838244166dSHyman Huang(黄勇) for (i = 0; i < stat->nvcpu; i++) { 1848244166dSHyman Huang(黄勇) dirtyrate = do_calculate_dirtyrate(records[i], duration); 1858244166dSHyman Huang(黄勇) 1868244166dSHyman Huang(黄勇) stat->rates[i].id = i; 1878244166dSHyman Huang(黄勇) stat->rates[i].dirty_rate = dirtyrate; 1888244166dSHyman Huang(黄勇) 1898244166dSHyman Huang(黄勇) trace_dirtyrate_do_calculate_vcpu(i, dirtyrate); 1908244166dSHyman Huang(黄勇) } 1918244166dSHyman Huang(黄勇) 1928244166dSHyman Huang(黄勇) g_free(records); 1938244166dSHyman Huang(黄勇) 1948244166dSHyman Huang(黄勇) return duration; 1958244166dSHyman Huang(黄勇) } 1968244166dSHyman Huang(黄勇) 19734a68001SAndrei Gudkov static bool is_calc_time_valid(int64_t msec) 198eca58224SChuan Zheng { 19934a68001SAndrei Gudkov if ((msec < MIN_CALC_TIME_MS) || (msec > MAX_CALC_TIME_MS)) { 200eca58224SChuan Zheng return false; 201eca58224SChuan Zheng } 202eca58224SChuan Zheng 203eca58224SChuan Zheng return true; 204eca58224SChuan Zheng } 205eca58224SChuan Zheng 2067afa08cdSHyman Huang(黄勇) static bool is_sample_pages_valid(int64_t pages) 2077afa08cdSHyman Huang(黄勇) { 2087afa08cdSHyman Huang(黄勇) return pages >= MIN_SAMPLE_PAGE_COUNT && 2097afa08cdSHyman Huang(黄勇) pages <= MAX_SAMPLE_PAGE_COUNT; 2107afa08cdSHyman Huang(黄勇) } 2117afa08cdSHyman Huang(黄勇) 2127df3aa30SChuan Zheng static int dirtyrate_set_state(int *state, int old_state, int new_state) 2137df3aa30SChuan Zheng { 2147df3aa30SChuan Zheng assert(new_state < DIRTY_RATE_STATUS__MAX); 2153c0b5dffSChuan Zheng trace_dirtyrate_set_state(DirtyRateStatus_str(new_state)); 2167df3aa30SChuan Zheng if (qatomic_cmpxchg(state, old_state, new_state) == old_state) { 2177df3aa30SChuan Zheng return 0; 2187df3aa30SChuan Zheng } else { 2197df3aa30SChuan Zheng return -1; 2207df3aa30SChuan Zheng } 2217df3aa30SChuan Zheng } 2227df3aa30SChuan Zheng 22334a68001SAndrei Gudkov /* Decimal power of given time unit relative to one second */ 22434a68001SAndrei Gudkov static int time_unit_to_power(TimeUnit time_unit) 22534a68001SAndrei Gudkov { 22634a68001SAndrei Gudkov switch (time_unit) { 22734a68001SAndrei Gudkov case TIME_UNIT_SECOND: 22834a68001SAndrei Gudkov return 0; 22934a68001SAndrei Gudkov case TIME_UNIT_MILLISECOND: 23034a68001SAndrei Gudkov return -3; 23134a68001SAndrei Gudkov default: 232fe1f1a80SPierrick Bouvier g_assert_not_reached(); 23334a68001SAndrei Gudkov } 23434a68001SAndrei Gudkov } 23534a68001SAndrei Gudkov 23634a68001SAndrei Gudkov static int64_t convert_time_unit(int64_t value, TimeUnit unit_from, 23734a68001SAndrei Gudkov TimeUnit unit_to) 23834a68001SAndrei Gudkov { 23934a68001SAndrei Gudkov int power = time_unit_to_power(unit_from) - 24034a68001SAndrei Gudkov time_unit_to_power(unit_to); 24134a68001SAndrei Gudkov while (power < 0) { 24234a68001SAndrei Gudkov value /= 10; 24334a68001SAndrei Gudkov power += 1; 24434a68001SAndrei Gudkov } 24534a68001SAndrei Gudkov while (power > 0) { 24634a68001SAndrei Gudkov value *= 10; 24734a68001SAndrei Gudkov power -= 1; 24834a68001SAndrei Gudkov } 24934a68001SAndrei Gudkov return value; 25034a68001SAndrei Gudkov } 25134a68001SAndrei Gudkov 25234a68001SAndrei Gudkov 25334a68001SAndrei Gudkov static struct DirtyRateInfo * 25434a68001SAndrei Gudkov query_dirty_rate_info(TimeUnit calc_time_unit) 2554c437254SChuan Zheng { 2560e21bf24SHyman Huang(黄勇) int i; 2574c437254SChuan Zheng int64_t dirty_rate = DirtyStat.dirty_rate; 258b21e2380SMarkus Armbruster struct DirtyRateInfo *info = g_new0(DirtyRateInfo, 1); 2590e21bf24SHyman Huang(黄勇) DirtyRateVcpuList *head = NULL, **tail = &head; 2604c437254SChuan Zheng 2614c437254SChuan Zheng info->status = CalculatingState; 2624c437254SChuan Zheng info->start_time = DirtyStat.start_time; 26334a68001SAndrei Gudkov info->calc_time = convert_time_unit(DirtyStat.calc_time_ms, 26434a68001SAndrei Gudkov TIME_UNIT_MILLISECOND, 26534a68001SAndrei Gudkov calc_time_unit); 26634a68001SAndrei Gudkov info->calc_time_unit = calc_time_unit; 2677afa08cdSHyman Huang(黄勇) info->sample_pages = DirtyStat.sample_pages; 2680e21bf24SHyman Huang(黄勇) info->mode = dirtyrate_mode; 2690e21bf24SHyman Huang(黄勇) 2700e21bf24SHyman Huang(黄勇) if (qatomic_read(&CalculatingState) == DIRTY_RATE_STATUS_MEASURED) { 2710e21bf24SHyman Huang(黄勇) info->has_dirty_rate = true; 2720e21bf24SHyman Huang(黄勇) info->dirty_rate = dirty_rate; 2730e21bf24SHyman Huang(黄勇) 2740e21bf24SHyman Huang(黄勇) if (dirtyrate_mode == DIRTY_RATE_MEASURE_MODE_DIRTY_RING) { 2750e21bf24SHyman Huang(黄勇) /* 2760e21bf24SHyman Huang(黄勇) * set sample_pages with 0 to indicate page sampling 2770e21bf24SHyman Huang(黄勇) * isn't enabled 2780e21bf24SHyman Huang(黄勇) **/ 2790e21bf24SHyman Huang(黄勇) info->sample_pages = 0; 2800e21bf24SHyman Huang(黄勇) info->has_vcpu_dirty_rate = true; 2810e21bf24SHyman Huang(黄勇) for (i = 0; i < DirtyStat.dirty_ring.nvcpu; i++) { 282b21e2380SMarkus Armbruster DirtyRateVcpu *rate = g_new0(DirtyRateVcpu, 1); 2830e21bf24SHyman Huang(黄勇) rate->id = DirtyStat.dirty_ring.rates[i].id; 2840e21bf24SHyman Huang(黄勇) rate->dirty_rate = DirtyStat.dirty_ring.rates[i].dirty_rate; 2850e21bf24SHyman Huang(黄勇) QAPI_LIST_APPEND(tail, rate); 2860e21bf24SHyman Huang(黄勇) } 2870e21bf24SHyman Huang(黄勇) info->vcpu_dirty_rate = head; 2880e21bf24SHyman Huang(黄勇) } 289826b8bc8SHyman Huang(黄勇) 290826b8bc8SHyman Huang(黄勇) if (dirtyrate_mode == DIRTY_RATE_MEASURE_MODE_DIRTY_BITMAP) { 291826b8bc8SHyman Huang(黄勇) info->sample_pages = 0; 292826b8bc8SHyman Huang(黄勇) } 2930e21bf24SHyman Huang(黄勇) } 2944c437254SChuan Zheng 2953c0b5dffSChuan Zheng trace_query_dirty_rate_info(DirtyRateStatus_str(CalculatingState)); 2963c0b5dffSChuan Zheng 2974c437254SChuan Zheng return info; 2984c437254SChuan Zheng } 2994c437254SChuan Zheng 300320a6cccSAndrei Gudkov static void init_dirtyrate_stat(struct DirtyRateConfig config) 301c9a58d71SChuan Zheng { 302c9a58d71SChuan Zheng DirtyStat.dirty_rate = -1; 303320a6cccSAndrei Gudkov DirtyStat.start_time = qemu_clock_get_ms(QEMU_CLOCK_HOST) / 1000; 30434a68001SAndrei Gudkov DirtyStat.calc_time_ms = config.calc_time_ms; 30571864eadSHyman Huang(黄勇) DirtyStat.sample_pages = config.sample_pages_per_gigabytes; 30671864eadSHyman Huang(黄勇) 30771864eadSHyman Huang(黄勇) switch (config.mode) { 30871864eadSHyman Huang(黄勇) case DIRTY_RATE_MEASURE_MODE_PAGE_SAMPLING: 30971864eadSHyman Huang(黄勇) DirtyStat.page_sampling.total_dirty_samples = 0; 31071864eadSHyman Huang(黄勇) DirtyStat.page_sampling.total_sample_count = 0; 31171864eadSHyman Huang(黄勇) DirtyStat.page_sampling.total_block_mem_MB = 0; 31271864eadSHyman Huang(黄勇) break; 31371864eadSHyman Huang(黄勇) case DIRTY_RATE_MEASURE_MODE_DIRTY_RING: 31471864eadSHyman Huang(黄勇) DirtyStat.dirty_ring.nvcpu = -1; 31571864eadSHyman Huang(黄勇) DirtyStat.dirty_ring.rates = NULL; 31671864eadSHyman Huang(黄勇) break; 31771864eadSHyman Huang(黄勇) default: 31871864eadSHyman Huang(黄勇) break; 31971864eadSHyman Huang(黄勇) } 320c9a58d71SChuan Zheng } 321c9a58d71SChuan Zheng 3220e21bf24SHyman Huang(黄勇) static void cleanup_dirtyrate_stat(struct DirtyRateConfig config) 3230e21bf24SHyman Huang(黄勇) { 3240e21bf24SHyman Huang(黄勇) /* last calc-dirty-rate qmp use dirty ring mode */ 3250e21bf24SHyman Huang(黄勇) if (dirtyrate_mode == DIRTY_RATE_MEASURE_MODE_DIRTY_RING) { 3260e21bf24SHyman Huang(黄勇) free(DirtyStat.dirty_ring.rates); 3270e21bf24SHyman Huang(黄勇) DirtyStat.dirty_ring.rates = NULL; 3280e21bf24SHyman Huang(黄勇) } 3290e21bf24SHyman Huang(黄勇) } 3300e21bf24SHyman Huang(黄勇) 331c9a58d71SChuan Zheng static void update_dirtyrate_stat(struct RamblockDirtyInfo *info) 332c9a58d71SChuan Zheng { 33371864eadSHyman Huang(黄勇) DirtyStat.page_sampling.total_dirty_samples += info->sample_dirty_count; 33471864eadSHyman Huang(黄勇) DirtyStat.page_sampling.total_sample_count += info->sample_pages_count; 335c9a58d71SChuan Zheng /* size of total pages in MB */ 336beeda9b7SJuan Quintela DirtyStat.page_sampling.total_block_mem_MB += 337beeda9b7SJuan Quintela qemu_target_pages_to_MiB(info->ramblock_pages); 338c9a58d71SChuan Zheng } 339c9a58d71SChuan Zheng 340c9a58d71SChuan Zheng static void update_dirtyrate(uint64_t msec) 341c9a58d71SChuan Zheng { 342c9a58d71SChuan Zheng uint64_t dirtyrate; 34371864eadSHyman Huang(黄勇) uint64_t total_dirty_samples = DirtyStat.page_sampling.total_dirty_samples; 34471864eadSHyman Huang(黄勇) uint64_t total_sample_count = DirtyStat.page_sampling.total_sample_count; 34571864eadSHyman Huang(黄勇) uint64_t total_block_mem_MB = DirtyStat.page_sampling.total_block_mem_MB; 346c9a58d71SChuan Zheng 347c9a58d71SChuan Zheng dirtyrate = total_dirty_samples * total_block_mem_MB * 348c9a58d71SChuan Zheng 1000 / (total_sample_count * msec); 349c9a58d71SChuan Zheng 350c9a58d71SChuan Zheng DirtyStat.dirty_rate = dirtyrate; 351c9a58d71SChuan Zheng } 3527df3aa30SChuan Zheng 353ba0e519fSChuan Zheng /* 35400a3f9c6SAndrei Gudkov * Compute hash of a single page of size TARGET_PAGE_SIZE. 35500a3f9c6SAndrei Gudkov */ 35600a3f9c6SAndrei Gudkov static uint32_t compute_page_hash(void *ptr) 35700a3f9c6SAndrei Gudkov { 358edd83a70SJuan Quintela size_t page_size = qemu_target_page_size(); 35900a3f9c6SAndrei Gudkov uint32_t i; 36000a3f9c6SAndrei Gudkov uint64_t v1, v2, v3, v4; 36100a3f9c6SAndrei Gudkov uint64_t res; 36200a3f9c6SAndrei Gudkov const uint64_t *p = ptr; 36300a3f9c6SAndrei Gudkov 36400a3f9c6SAndrei Gudkov v1 = QEMU_XXHASH_SEED + XXH_PRIME64_1 + XXH_PRIME64_2; 36500a3f9c6SAndrei Gudkov v2 = QEMU_XXHASH_SEED + XXH_PRIME64_2; 36600a3f9c6SAndrei Gudkov v3 = QEMU_XXHASH_SEED + 0; 36700a3f9c6SAndrei Gudkov v4 = QEMU_XXHASH_SEED - XXH_PRIME64_1; 368edd83a70SJuan Quintela for (i = 0; i < page_size / 8; i += 4) { 36900a3f9c6SAndrei Gudkov v1 = XXH64_round(v1, p[i + 0]); 37000a3f9c6SAndrei Gudkov v2 = XXH64_round(v2, p[i + 1]); 37100a3f9c6SAndrei Gudkov v3 = XXH64_round(v3, p[i + 2]); 37200a3f9c6SAndrei Gudkov v4 = XXH64_round(v4, p[i + 3]); 37300a3f9c6SAndrei Gudkov } 37400a3f9c6SAndrei Gudkov res = XXH64_mergerounds(v1, v2, v3, v4); 375edd83a70SJuan Quintela res += page_size; 37600a3f9c6SAndrei Gudkov res = XXH64_avalanche(res); 37700a3f9c6SAndrei Gudkov return (uint32_t)(res & UINT32_MAX); 37800a3f9c6SAndrei Gudkov } 37900a3f9c6SAndrei Gudkov 38000a3f9c6SAndrei Gudkov 38100a3f9c6SAndrei Gudkov /* 382ba0e519fSChuan Zheng * get hash result for the sampled memory with length of TARGET_PAGE_SIZE 383ba0e519fSChuan Zheng * in ramblock, which starts from ramblock base address. 384ba0e519fSChuan Zheng */ 385ba0e519fSChuan Zheng static uint32_t get_ramblock_vfn_hash(struct RamblockDirtyInfo *info, 386ba0e519fSChuan Zheng uint64_t vfn) 387ba0e519fSChuan Zheng { 38800a3f9c6SAndrei Gudkov uint32_t hash; 389ba0e519fSChuan Zheng 390edd83a70SJuan Quintela hash = compute_page_hash(info->ramblock_addr + 391edd83a70SJuan Quintela vfn * qemu_target_page_size()); 392ba0e519fSChuan Zheng 39300a3f9c6SAndrei Gudkov trace_get_ramblock_vfn_hash(info->idstr, vfn, hash); 39400a3f9c6SAndrei Gudkov return hash; 395ba0e519fSChuan Zheng } 396ba0e519fSChuan Zheng 397ba0e519fSChuan Zheng static bool save_ramblock_hash(struct RamblockDirtyInfo *info) 398ba0e519fSChuan Zheng { 399ba0e519fSChuan Zheng unsigned int sample_pages_count; 400ba0e519fSChuan Zheng int i; 401ba0e519fSChuan Zheng GRand *rand; 402ba0e519fSChuan Zheng 403ba0e519fSChuan Zheng sample_pages_count = info->sample_pages_count; 404ba0e519fSChuan Zheng 405ba0e519fSChuan Zheng /* ramblock size less than one page, return success to skip this ramblock */ 406ba0e519fSChuan Zheng if (unlikely(info->ramblock_pages == 0 || sample_pages_count == 0)) { 407ba0e519fSChuan Zheng return true; 408ba0e519fSChuan Zheng } 409ba0e519fSChuan Zheng 410ba0e519fSChuan Zheng info->hash_result = g_try_malloc0_n(sample_pages_count, 411ba0e519fSChuan Zheng sizeof(uint32_t)); 412ba0e519fSChuan Zheng if (!info->hash_result) { 413ba0e519fSChuan Zheng return false; 414ba0e519fSChuan Zheng } 415ba0e519fSChuan Zheng 416ba0e519fSChuan Zheng info->sample_page_vfn = g_try_malloc0_n(sample_pages_count, 417ba0e519fSChuan Zheng sizeof(uint64_t)); 418ba0e519fSChuan Zheng if (!info->sample_page_vfn) { 419ba0e519fSChuan Zheng g_free(info->hash_result); 420ba0e519fSChuan Zheng return false; 421ba0e519fSChuan Zheng } 422ba0e519fSChuan Zheng 423ba0e519fSChuan Zheng rand = g_rand_new(); 424ba0e519fSChuan Zheng for (i = 0; i < sample_pages_count; i++) { 425ba0e519fSChuan Zheng info->sample_page_vfn[i] = g_rand_int_range(rand, 0, 426ba0e519fSChuan Zheng info->ramblock_pages - 1); 427ba0e519fSChuan Zheng info->hash_result[i] = get_ramblock_vfn_hash(info, 428ba0e519fSChuan Zheng info->sample_page_vfn[i]); 429ba0e519fSChuan Zheng } 430ba0e519fSChuan Zheng g_rand_free(rand); 431ba0e519fSChuan Zheng 432ba0e519fSChuan Zheng return true; 433ba0e519fSChuan Zheng } 434ba0e519fSChuan Zheng 435ba0e519fSChuan Zheng static void get_ramblock_dirty_info(RAMBlock *block, 436ba0e519fSChuan Zheng struct RamblockDirtyInfo *info, 437ba0e519fSChuan Zheng struct DirtyRateConfig *config) 438ba0e519fSChuan Zheng { 439ba0e519fSChuan Zheng uint64_t sample_pages_per_gigabytes = config->sample_pages_per_gigabytes; 44088c3b57fSThomas Huth gsize len; 441ba0e519fSChuan Zheng 442ba0e519fSChuan Zheng /* Right shift 30 bits to calc ramblock size in GB */ 443ba0e519fSChuan Zheng info->sample_pages_count = (qemu_ram_get_used_length(block) * 444ba0e519fSChuan Zheng sample_pages_per_gigabytes) >> 30; 445ba0e519fSChuan Zheng /* Right shift TARGET_PAGE_BITS to calc page count */ 446ba0e519fSChuan Zheng info->ramblock_pages = qemu_ram_get_used_length(block) >> 447148b1ad8SJuan Quintela qemu_target_page_bits(); 448ba0e519fSChuan Zheng info->ramblock_addr = qemu_ram_get_host_addr(block); 44988c3b57fSThomas Huth len = g_strlcpy(info->idstr, qemu_ram_get_idstr(block), 45088c3b57fSThomas Huth sizeof(info->idstr)); 45188c3b57fSThomas Huth g_assert(len < sizeof(info->idstr)); 452ba0e519fSChuan Zheng } 453ba0e519fSChuan Zheng 454cf0bbb49SChuan Zheng static void free_ramblock_dirty_info(struct RamblockDirtyInfo *infos, int count) 455cf0bbb49SChuan Zheng { 456cf0bbb49SChuan Zheng int i; 457cf0bbb49SChuan Zheng 458cf0bbb49SChuan Zheng if (!infos) { 459cf0bbb49SChuan Zheng return; 460cf0bbb49SChuan Zheng } 461cf0bbb49SChuan Zheng 462cf0bbb49SChuan Zheng for (i = 0; i < count; i++) { 463cf0bbb49SChuan Zheng g_free(infos[i].sample_page_vfn); 464cf0bbb49SChuan Zheng g_free(infos[i].hash_result); 465cf0bbb49SChuan Zheng } 466cf0bbb49SChuan Zheng g_free(infos); 467cf0bbb49SChuan Zheng } 468cf0bbb49SChuan Zheng 469f82583cdSChuan Zheng static bool skip_sample_ramblock(RAMBlock *block) 470f82583cdSChuan Zheng { 471f82583cdSChuan Zheng /* 472f82583cdSChuan Zheng * Sample only blocks larger than MIN_RAMBLOCK_SIZE. 473f82583cdSChuan Zheng */ 474f82583cdSChuan Zheng if (qemu_ram_get_used_length(block) < (MIN_RAMBLOCK_SIZE << 10)) { 4753c0b5dffSChuan Zheng trace_skip_sample_ramblock(block->idstr, 4763c0b5dffSChuan Zheng qemu_ram_get_used_length(block)); 477f82583cdSChuan Zheng return true; 478f82583cdSChuan Zheng } 479f82583cdSChuan Zheng 480f82583cdSChuan Zheng return false; 481f82583cdSChuan Zheng } 482f82583cdSChuan Zheng 483ba0e519fSChuan Zheng static bool record_ramblock_hash_info(struct RamblockDirtyInfo **block_dinfo, 484ba0e519fSChuan Zheng struct DirtyRateConfig config, 485ba0e519fSChuan Zheng int *block_count) 486ba0e519fSChuan Zheng { 487ba0e519fSChuan Zheng struct RamblockDirtyInfo *info = NULL; 488ba0e519fSChuan Zheng struct RamblockDirtyInfo *dinfo = NULL; 489ba0e519fSChuan Zheng RAMBlock *block = NULL; 490ba0e519fSChuan Zheng int total_count = 0; 491ba0e519fSChuan Zheng int index = 0; 492ba0e519fSChuan Zheng bool ret = false; 493ba0e519fSChuan Zheng 494ba0e519fSChuan Zheng RAMBLOCK_FOREACH_MIGRATABLE(block) { 495f82583cdSChuan Zheng if (skip_sample_ramblock(block)) { 496f82583cdSChuan Zheng continue; 497f82583cdSChuan Zheng } 498ba0e519fSChuan Zheng total_count++; 499ba0e519fSChuan Zheng } 500ba0e519fSChuan Zheng 501ba0e519fSChuan Zheng dinfo = g_try_malloc0_n(total_count, sizeof(struct RamblockDirtyInfo)); 502ba0e519fSChuan Zheng if (dinfo == NULL) { 503ba0e519fSChuan Zheng goto out; 504ba0e519fSChuan Zheng } 505ba0e519fSChuan Zheng 506ba0e519fSChuan Zheng RAMBLOCK_FOREACH_MIGRATABLE(block) { 507f82583cdSChuan Zheng if (skip_sample_ramblock(block)) { 508f82583cdSChuan Zheng continue; 509f82583cdSChuan Zheng } 510ba0e519fSChuan Zheng if (index >= total_count) { 511ba0e519fSChuan Zheng break; 512ba0e519fSChuan Zheng } 513ba0e519fSChuan Zheng info = &dinfo[index]; 514ba0e519fSChuan Zheng get_ramblock_dirty_info(block, info, &config); 515ba0e519fSChuan Zheng if (!save_ramblock_hash(info)) { 516ba0e519fSChuan Zheng goto out; 517ba0e519fSChuan Zheng } 518ba0e519fSChuan Zheng index++; 519ba0e519fSChuan Zheng } 520ba0e519fSChuan Zheng ret = true; 521ba0e519fSChuan Zheng 522ba0e519fSChuan Zheng out: 523ba0e519fSChuan Zheng *block_count = index; 524ba0e519fSChuan Zheng *block_dinfo = dinfo; 525ba0e519fSChuan Zheng return ret; 526ba0e519fSChuan Zheng } 527ba0e519fSChuan Zheng 5289c04387bSChuan Zheng static void calc_page_dirty_rate(struct RamblockDirtyInfo *info) 5299c04387bSChuan Zheng { 53000a3f9c6SAndrei Gudkov uint32_t hash; 5319c04387bSChuan Zheng int i; 5329c04387bSChuan Zheng 5339c04387bSChuan Zheng for (i = 0; i < info->sample_pages_count; i++) { 53400a3f9c6SAndrei Gudkov hash = get_ramblock_vfn_hash(info, info->sample_page_vfn[i]); 53500a3f9c6SAndrei Gudkov if (hash != info->hash_result[i]) { 53600a3f9c6SAndrei Gudkov trace_calc_page_dirty_rate(info->idstr, hash, info->hash_result[i]); 5379c04387bSChuan Zheng info->sample_dirty_count++; 5389c04387bSChuan Zheng } 5399c04387bSChuan Zheng } 5409c04387bSChuan Zheng } 5419c04387bSChuan Zheng 5429c04387bSChuan Zheng static struct RamblockDirtyInfo * 5439c04387bSChuan Zheng find_block_matched(RAMBlock *block, int count, 5449c04387bSChuan Zheng struct RamblockDirtyInfo *infos) 5459c04387bSChuan Zheng { 5469c04387bSChuan Zheng int i; 5479c04387bSChuan Zheng 5489c04387bSChuan Zheng for (i = 0; i < count; i++) { 5499c04387bSChuan Zheng if (!strcmp(infos[i].idstr, qemu_ram_get_idstr(block))) { 5509c04387bSChuan Zheng break; 5519c04387bSChuan Zheng } 5529c04387bSChuan Zheng } 5539c04387bSChuan Zheng 5549c04387bSChuan Zheng if (i == count) { 5559c04387bSChuan Zheng return NULL; 5569c04387bSChuan Zheng } 5579c04387bSChuan Zheng 5589c04387bSChuan Zheng if (infos[i].ramblock_addr != qemu_ram_get_host_addr(block) || 5599c04387bSChuan Zheng infos[i].ramblock_pages != 560148b1ad8SJuan Quintela (qemu_ram_get_used_length(block) >> qemu_target_page_bits())) { 5613c0b5dffSChuan Zheng trace_find_page_matched(block->idstr); 5629c04387bSChuan Zheng return NULL; 5639c04387bSChuan Zheng } 5649c04387bSChuan Zheng 56566997c42SMarkus Armbruster return &infos[i]; 5669c04387bSChuan Zheng } 5679c04387bSChuan Zheng 5689c04387bSChuan Zheng static bool compare_page_hash_info(struct RamblockDirtyInfo *info, 5699c04387bSChuan Zheng int block_count) 5709c04387bSChuan Zheng { 5719c04387bSChuan Zheng struct RamblockDirtyInfo *block_dinfo = NULL; 5729c04387bSChuan Zheng RAMBlock *block = NULL; 5739c04387bSChuan Zheng 5749c04387bSChuan Zheng RAMBLOCK_FOREACH_MIGRATABLE(block) { 575f82583cdSChuan Zheng if (skip_sample_ramblock(block)) { 576f82583cdSChuan Zheng continue; 577f82583cdSChuan Zheng } 5789c04387bSChuan Zheng block_dinfo = find_block_matched(block, block_count, info); 5799c04387bSChuan Zheng if (block_dinfo == NULL) { 5809c04387bSChuan Zheng continue; 5819c04387bSChuan Zheng } 5829c04387bSChuan Zheng calc_page_dirty_rate(block_dinfo); 5839c04387bSChuan Zheng update_dirtyrate_stat(block_dinfo); 5849c04387bSChuan Zheng } 5859c04387bSChuan Zheng 58671864eadSHyman Huang(黄勇) if (DirtyStat.page_sampling.total_sample_count == 0) { 5879c04387bSChuan Zheng return false; 5889c04387bSChuan Zheng } 5899c04387bSChuan Zheng 5909c04387bSChuan Zheng return true; 5919c04387bSChuan Zheng } 5929c04387bSChuan Zheng 593826b8bc8SHyman Huang(黄勇) static inline void record_dirtypages_bitmap(DirtyPageRecord *dirty_pages, 594826b8bc8SHyman Huang(黄勇) bool start) 595826b8bc8SHyman Huang(黄勇) { 596826b8bc8SHyman Huang(黄勇) if (start) { 597826b8bc8SHyman Huang(黄勇) dirty_pages->start_pages = total_dirty_pages; 598826b8bc8SHyman Huang(黄勇) } else { 599826b8bc8SHyman Huang(黄勇) dirty_pages->end_pages = total_dirty_pages; 600826b8bc8SHyman Huang(黄勇) } 601826b8bc8SHyman Huang(黄勇) } 602826b8bc8SHyman Huang(黄勇) 603826b8bc8SHyman Huang(黄勇) static inline void dirtyrate_manual_reset_protect(void) 604826b8bc8SHyman Huang(黄勇) { 605826b8bc8SHyman Huang(黄勇) RAMBlock *block = NULL; 606826b8bc8SHyman Huang(黄勇) 607826b8bc8SHyman Huang(黄勇) WITH_RCU_READ_LOCK_GUARD() { 608826b8bc8SHyman Huang(黄勇) RAMBLOCK_FOREACH_MIGRATABLE(block) { 609826b8bc8SHyman Huang(黄勇) memory_region_clear_dirty_bitmap(block->mr, 0, 610826b8bc8SHyman Huang(黄勇) block->used_length); 611826b8bc8SHyman Huang(黄勇) } 612826b8bc8SHyman Huang(黄勇) } 613826b8bc8SHyman Huang(黄勇) } 614826b8bc8SHyman Huang(黄勇) 615826b8bc8SHyman Huang(黄勇) static void calculate_dirtyrate_dirty_bitmap(struct DirtyRateConfig config) 616826b8bc8SHyman Huang(黄勇) { 617826b8bc8SHyman Huang(黄勇) int64_t start_time; 618826b8bc8SHyman Huang(黄勇) DirtyPageRecord dirty_pages; 619639ec3fbSCédric Le Goater Error *local_err = NULL; 620826b8bc8SHyman Huang(黄勇) 621195801d7SStefan Hajnoczi bql_lock(); 622639ec3fbSCédric Le Goater if (!memory_global_dirty_log_start(GLOBAL_DIRTY_DIRTY_RATE, &local_err)) { 623639ec3fbSCédric Le Goater error_report_err(local_err); 624639ec3fbSCédric Le Goater } 625826b8bc8SHyman Huang(黄勇) 626826b8bc8SHyman Huang(黄勇) /* 627826b8bc8SHyman Huang(黄勇) * 1'round of log sync may return all 1 bits with 628826b8bc8SHyman Huang(黄勇) * KVM_DIRTY_LOG_INITIALLY_SET enable 629826b8bc8SHyman Huang(黄勇) * skip it unconditionally and start dirty tracking 630826b8bc8SHyman Huang(黄勇) * from 2'round of log sync 631826b8bc8SHyman Huang(黄勇) */ 6321e493be5SGavin Shan memory_global_dirty_log_sync(false); 633826b8bc8SHyman Huang(黄勇) 634826b8bc8SHyman Huang(黄勇) /* 635826b8bc8SHyman Huang(黄勇) * reset page protect manually and unconditionally. 636826b8bc8SHyman Huang(黄勇) * this make sure kvm dirty log be cleared if 637826b8bc8SHyman Huang(黄勇) * KVM_DIRTY_LOG_MANUAL_PROTECT_ENABLE cap is enabled. 638826b8bc8SHyman Huang(黄勇) */ 639826b8bc8SHyman Huang(黄勇) dirtyrate_manual_reset_protect(); 640195801d7SStefan Hajnoczi bql_unlock(); 641826b8bc8SHyman Huang(黄勇) 642826b8bc8SHyman Huang(黄勇) record_dirtypages_bitmap(&dirty_pages, true); 643826b8bc8SHyman Huang(黄勇) 644826b8bc8SHyman Huang(黄勇) start_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); 645320a6cccSAndrei Gudkov DirtyStat.start_time = qemu_clock_get_ms(QEMU_CLOCK_HOST) / 1000; 646826b8bc8SHyman Huang(黄勇) 64734a68001SAndrei Gudkov DirtyStat.calc_time_ms = dirty_stat_wait(config.calc_time_ms, start_time); 648826b8bc8SHyman Huang(黄勇) 649826b8bc8SHyman Huang(黄勇) /* 6508244166dSHyman Huang(黄勇) * do two things. 651826b8bc8SHyman Huang(黄勇) * 1. fetch dirty bitmap from kvm 652826b8bc8SHyman Huang(黄勇) * 2. stop dirty tracking 653826b8bc8SHyman Huang(黄勇) */ 6548244166dSHyman Huang(黄勇) global_dirty_log_sync(GLOBAL_DIRTY_DIRTY_RATE, true); 655826b8bc8SHyman Huang(黄勇) 656826b8bc8SHyman Huang(黄勇) record_dirtypages_bitmap(&dirty_pages, false); 657826b8bc8SHyman Huang(黄勇) 65834a68001SAndrei Gudkov DirtyStat.dirty_rate = do_calculate_dirtyrate(dirty_pages, 65934a68001SAndrei Gudkov DirtyStat.calc_time_ms); 660826b8bc8SHyman Huang(黄勇) } 661826b8bc8SHyman Huang(黄勇) 6620e21bf24SHyman Huang(黄勇) static void calculate_dirtyrate_dirty_ring(struct DirtyRateConfig config) 6630e21bf24SHyman Huang(黄勇) { 6640e21bf24SHyman Huang(黄勇) uint64_t dirtyrate = 0; 6650e21bf24SHyman Huang(黄勇) uint64_t dirtyrate_sum = 0; 6660e21bf24SHyman Huang(黄勇) int i = 0; 6670e21bf24SHyman Huang(黄勇) 6688244166dSHyman Huang(黄勇) /* start log sync */ 6698244166dSHyman Huang(黄勇) global_dirty_log_change(GLOBAL_DIRTY_DIRTY_RATE, true); 6700e21bf24SHyman Huang(黄勇) 671320a6cccSAndrei Gudkov DirtyStat.start_time = qemu_clock_get_ms(QEMU_CLOCK_HOST) / 1000; 6720e21bf24SHyman Huang(黄勇) 6738244166dSHyman Huang(黄勇) /* calculate vcpu dirtyrate */ 67434a68001SAndrei Gudkov DirtyStat.calc_time_ms = vcpu_calculate_dirtyrate(config.calc_time_ms, 6758244166dSHyman Huang(黄勇) &DirtyStat.dirty_ring, 6768244166dSHyman Huang(黄勇) GLOBAL_DIRTY_DIRTY_RATE, 6778244166dSHyman Huang(黄勇) true); 6780e21bf24SHyman Huang(黄勇) 6798244166dSHyman Huang(黄勇) /* calculate vm dirtyrate */ 6800e21bf24SHyman Huang(黄勇) for (i = 0; i < DirtyStat.dirty_ring.nvcpu; i++) { 6818244166dSHyman Huang(黄勇) dirtyrate = DirtyStat.dirty_ring.rates[i].dirty_rate; 6820e21bf24SHyman Huang(黄勇) DirtyStat.dirty_ring.rates[i].dirty_rate = dirtyrate; 6830e21bf24SHyman Huang(黄勇) dirtyrate_sum += dirtyrate; 6840e21bf24SHyman Huang(黄勇) } 6850e21bf24SHyman Huang(黄勇) 6860e21bf24SHyman Huang(黄勇) DirtyStat.dirty_rate = dirtyrate_sum; 6870e21bf24SHyman Huang(黄勇) } 6880e21bf24SHyman Huang(黄勇) 6890e21bf24SHyman Huang(黄勇) static void calculate_dirtyrate_sample_vm(struct DirtyRateConfig config) 6904240dceeSChuan Zheng { 691cf0bbb49SChuan Zheng struct RamblockDirtyInfo *block_dinfo = NULL; 692cf0bbb49SChuan Zheng int block_count = 0; 693cf0bbb49SChuan Zheng int64_t initial_time; 694cf0bbb49SChuan Zheng 695cf0bbb49SChuan Zheng rcu_read_lock(); 696cf0bbb49SChuan Zheng initial_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); 697320a6cccSAndrei Gudkov DirtyStat.start_time = qemu_clock_get_ms(QEMU_CLOCK_HOST) / 1000; 698cf0bbb49SChuan Zheng if (!record_ramblock_hash_info(&block_dinfo, config, &block_count)) { 699cf0bbb49SChuan Zheng goto out; 700cf0bbb49SChuan Zheng } 701cf0bbb49SChuan Zheng rcu_read_unlock(); 702cf0bbb49SChuan Zheng 70334a68001SAndrei Gudkov DirtyStat.calc_time_ms = dirty_stat_wait(config.calc_time_ms, 70434a68001SAndrei Gudkov initial_time); 705cf0bbb49SChuan Zheng 706cf0bbb49SChuan Zheng rcu_read_lock(); 707cf0bbb49SChuan Zheng if (!compare_page_hash_info(block_dinfo, block_count)) { 708cf0bbb49SChuan Zheng goto out; 709cf0bbb49SChuan Zheng } 710cf0bbb49SChuan Zheng 71134a68001SAndrei Gudkov update_dirtyrate(DirtyStat.calc_time_ms); 712cf0bbb49SChuan Zheng 713cf0bbb49SChuan Zheng out: 714cf0bbb49SChuan Zheng rcu_read_unlock(); 715cf0bbb49SChuan Zheng free_ramblock_dirty_info(block_dinfo, block_count); 7164240dceeSChuan Zheng } 7174240dceeSChuan Zheng 7180e21bf24SHyman Huang(黄勇) static void calculate_dirtyrate(struct DirtyRateConfig config) 7190e21bf24SHyman Huang(黄勇) { 720826b8bc8SHyman Huang(黄勇) if (config.mode == DIRTY_RATE_MEASURE_MODE_DIRTY_BITMAP) { 721826b8bc8SHyman Huang(黄勇) calculate_dirtyrate_dirty_bitmap(config); 722826b8bc8SHyman Huang(黄勇) } else if (config.mode == DIRTY_RATE_MEASURE_MODE_DIRTY_RING) { 7230e21bf24SHyman Huang(黄勇) calculate_dirtyrate_dirty_ring(config); 7240e21bf24SHyman Huang(黄勇) } else { 7250e21bf24SHyman Huang(黄勇) calculate_dirtyrate_sample_vm(config); 7260e21bf24SHyman Huang(黄勇) } 7270e21bf24SHyman Huang(黄勇) 7280e21bf24SHyman Huang(黄勇) trace_dirtyrate_calculate(DirtyStat.dirty_rate); 7290e21bf24SHyman Huang(黄勇) } 7300e21bf24SHyman Huang(黄勇) 7314240dceeSChuan Zheng void *get_dirtyrate_thread(void *arg) 7324240dceeSChuan Zheng { 7334240dceeSChuan Zheng struct DirtyRateConfig config = *(struct DirtyRateConfig *)arg; 7347df3aa30SChuan Zheng int ret; 73515eb2d64SHyman Huang(黄勇) rcu_register_thread(); 7367df3aa30SChuan Zheng 7377df3aa30SChuan Zheng ret = dirtyrate_set_state(&CalculatingState, DIRTY_RATE_STATUS_UNSTARTED, 7387df3aa30SChuan Zheng DIRTY_RATE_STATUS_MEASURING); 7397df3aa30SChuan Zheng if (ret == -1) { 7407df3aa30SChuan Zheng error_report("change dirtyrate state failed."); 7417df3aa30SChuan Zheng return NULL; 7427df3aa30SChuan Zheng } 7434240dceeSChuan Zheng 7444240dceeSChuan Zheng calculate_dirtyrate(config); 7454240dceeSChuan Zheng 7467df3aa30SChuan Zheng ret = dirtyrate_set_state(&CalculatingState, DIRTY_RATE_STATUS_MEASURING, 7477df3aa30SChuan Zheng DIRTY_RATE_STATUS_MEASURED); 7487df3aa30SChuan Zheng if (ret == -1) { 7497df3aa30SChuan Zheng error_report("change dirtyrate state failed."); 7507df3aa30SChuan Zheng } 75115eb2d64SHyman Huang(黄勇) 75215eb2d64SHyman Huang(黄勇) rcu_unregister_thread(); 7534240dceeSChuan Zheng return NULL; 7544240dceeSChuan Zheng } 7554c437254SChuan Zheng 7560e21bf24SHyman Huang(黄勇) void qmp_calc_dirty_rate(int64_t calc_time, 75734a68001SAndrei Gudkov bool has_calc_time_unit, 75834a68001SAndrei Gudkov TimeUnit calc_time_unit, 7590e21bf24SHyman Huang(黄勇) bool has_sample_pages, 7600e21bf24SHyman Huang(黄勇) int64_t sample_pages, 7610e21bf24SHyman Huang(黄勇) bool has_mode, 7620e21bf24SHyman Huang(黄勇) DirtyRateMeasureMode mode, 7630e21bf24SHyman Huang(黄勇) Error **errp) 7644c437254SChuan Zheng { 7654c437254SChuan Zheng static struct DirtyRateConfig config; 7664c437254SChuan Zheng QemuThread thread; 7674c437254SChuan Zheng int ret; 7684c437254SChuan Zheng 7694c437254SChuan Zheng /* 7704c437254SChuan Zheng * If the dirty rate is already being measured, don't attempt to start. 7714c437254SChuan Zheng */ 7724c437254SChuan Zheng if (qatomic_read(&CalculatingState) == DIRTY_RATE_STATUS_MEASURING) { 7734c437254SChuan Zheng error_setg(errp, "the dirty rate is already being measured."); 7744c437254SChuan Zheng return; 7754c437254SChuan Zheng } 7764c437254SChuan Zheng 77734a68001SAndrei Gudkov int64_t calc_time_ms = convert_time_unit( 77834a68001SAndrei Gudkov calc_time, 77934a68001SAndrei Gudkov has_calc_time_unit ? calc_time_unit : TIME_UNIT_SECOND, 78034a68001SAndrei Gudkov TIME_UNIT_MILLISECOND 78134a68001SAndrei Gudkov ); 78234a68001SAndrei Gudkov 78334a68001SAndrei Gudkov if (!is_calc_time_valid(calc_time_ms)) { 78434a68001SAndrei Gudkov error_setg(errp, "Calculation time is out of range [%dms, %dms].", 78534a68001SAndrei Gudkov MIN_CALC_TIME_MS, MAX_CALC_TIME_MS); 7864c437254SChuan Zheng return; 7874c437254SChuan Zheng } 7884c437254SChuan Zheng 7890e21bf24SHyman Huang(黄勇) if (!has_mode) { 7900e21bf24SHyman Huang(黄勇) mode = DIRTY_RATE_MEASURE_MODE_PAGE_SAMPLING; 7910e21bf24SHyman Huang(黄勇) } 7920e21bf24SHyman Huang(黄勇) 793bd9510d3SZhenzhong Duan if (has_sample_pages && mode != DIRTY_RATE_MEASURE_MODE_PAGE_SAMPLING) { 794bd9510d3SZhenzhong Duan error_setg(errp, "sample-pages is used only in page-sampling mode"); 7950e21bf24SHyman Huang(黄勇) return; 7960e21bf24SHyman Huang(黄勇) } 7970e21bf24SHyman Huang(黄勇) 7987afa08cdSHyman Huang(黄勇) if (has_sample_pages) { 7997afa08cdSHyman Huang(黄勇) if (!is_sample_pages_valid(sample_pages)) { 8007afa08cdSHyman Huang(黄勇) error_setg(errp, "sample-pages is out of range[%d, %d].", 8017afa08cdSHyman Huang(黄勇) MIN_SAMPLE_PAGE_COUNT, 8027afa08cdSHyman Huang(黄勇) MAX_SAMPLE_PAGE_COUNT); 8037afa08cdSHyman Huang(黄勇) return; 8047afa08cdSHyman Huang(黄勇) } 8057afa08cdSHyman Huang(黄勇) } else { 8067afa08cdSHyman Huang(黄勇) sample_pages = DIRTYRATE_DEFAULT_SAMPLE_PAGES; 8077afa08cdSHyman Huang(黄勇) } 8087afa08cdSHyman Huang(黄勇) 8094c437254SChuan Zheng /* 8100e21bf24SHyman Huang(黄勇) * dirty ring mode only works when kvm dirty ring is enabled. 811826b8bc8SHyman Huang(黄勇) * on the contrary, dirty bitmap mode is not. 8120e21bf24SHyman Huang(黄勇) */ 813826b8bc8SHyman Huang(黄勇) if (((mode == DIRTY_RATE_MEASURE_MODE_DIRTY_RING) && 814826b8bc8SHyman Huang(黄勇) !kvm_dirty_ring_enabled()) || 815826b8bc8SHyman Huang(黄勇) ((mode == DIRTY_RATE_MEASURE_MODE_DIRTY_BITMAP) && 816826b8bc8SHyman Huang(黄勇) kvm_dirty_ring_enabled())) { 817826b8bc8SHyman Huang(黄勇) error_setg(errp, "mode %s is not enabled, use other method instead.", 818826b8bc8SHyman Huang(黄勇) DirtyRateMeasureMode_str(mode)); 8190e21bf24SHyman Huang(黄勇) return; 8200e21bf24SHyman Huang(黄勇) } 8210e21bf24SHyman Huang(黄勇) 8220e21bf24SHyman Huang(黄勇) /* 8234c437254SChuan Zheng * Init calculation state as unstarted. 8244c437254SChuan Zheng */ 8254c437254SChuan Zheng ret = dirtyrate_set_state(&CalculatingState, CalculatingState, 8264c437254SChuan Zheng DIRTY_RATE_STATUS_UNSTARTED); 8274c437254SChuan Zheng if (ret == -1) { 8284c437254SChuan Zheng error_setg(errp, "init dirty rate calculation state failed."); 8294c437254SChuan Zheng return; 8304c437254SChuan Zheng } 8314c437254SChuan Zheng 83234a68001SAndrei Gudkov config.calc_time_ms = calc_time_ms; 8337afa08cdSHyman Huang(黄勇) config.sample_pages_per_gigabytes = sample_pages; 8340e21bf24SHyman Huang(黄勇) config.mode = mode; 8350e21bf24SHyman Huang(黄勇) 8360e21bf24SHyman Huang(黄勇) cleanup_dirtyrate_stat(config); 8370e21bf24SHyman Huang(黄勇) 8380e21bf24SHyman Huang(黄勇) /* 8390e21bf24SHyman Huang(黄勇) * update dirty rate mode so that we can figure out what mode has 8400e21bf24SHyman Huang(黄勇) * been used in last calculation 8410e21bf24SHyman Huang(黄勇) **/ 8420e21bf24SHyman Huang(黄勇) dirtyrate_mode = mode; 8439865d0f6SHyman Huang(黄勇) 844320a6cccSAndrei Gudkov init_dirtyrate_stat(config); 8459865d0f6SHyman Huang(黄勇) 846e620b1e4SPeter Xu qemu_thread_create(&thread, MIGRATION_THREAD_DIRTY_RATE, 847e620b1e4SPeter Xu get_dirtyrate_thread, (void *)&config, 848e620b1e4SPeter Xu QEMU_THREAD_DETACHED); 8494c437254SChuan Zheng } 8504c437254SChuan Zheng 85134a68001SAndrei Gudkov 85234a68001SAndrei Gudkov struct DirtyRateInfo *qmp_query_dirty_rate(bool has_calc_time_unit, 85334a68001SAndrei Gudkov TimeUnit calc_time_unit, 85434a68001SAndrei Gudkov Error **errp) 8554c437254SChuan Zheng { 85634a68001SAndrei Gudkov return query_dirty_rate_info( 85734a68001SAndrei Gudkov has_calc_time_unit ? calc_time_unit : TIME_UNIT_SECOND); 8584c437254SChuan Zheng } 859a4a571d9SPeter Xu 860a4a571d9SPeter Xu void hmp_info_dirty_rate(Monitor *mon, const QDict *qdict) 861a4a571d9SPeter Xu { 86234a68001SAndrei Gudkov DirtyRateInfo *info = query_dirty_rate_info(TIME_UNIT_SECOND); 863a4a571d9SPeter Xu 864a4a571d9SPeter Xu monitor_printf(mon, "Status: %s\n", 865a4a571d9SPeter Xu DirtyRateStatus_str(info->status)); 866a4a571d9SPeter Xu monitor_printf(mon, "Start Time: %"PRIi64" (ms)\n", 867a4a571d9SPeter Xu info->start_time); 868bd9510d3SZhenzhong Duan if (info->mode == DIRTY_RATE_MEASURE_MODE_PAGE_SAMPLING) { 869a4a571d9SPeter Xu monitor_printf(mon, "Sample Pages: %"PRIu64" (per GB)\n", 870a4a571d9SPeter Xu info->sample_pages); 871bd9510d3SZhenzhong Duan } 872a4a571d9SPeter Xu monitor_printf(mon, "Period: %"PRIi64" (sec)\n", 873a4a571d9SPeter Xu info->calc_time); 8740e21bf24SHyman Huang(黄勇) monitor_printf(mon, "Mode: %s\n", 8750e21bf24SHyman Huang(黄勇) DirtyRateMeasureMode_str(info->mode)); 876a4a571d9SPeter Xu monitor_printf(mon, "Dirty rate: "); 877a4a571d9SPeter Xu if (info->has_dirty_rate) { 878a4a571d9SPeter Xu monitor_printf(mon, "%"PRIi64" (MB/s)\n", info->dirty_rate); 8790e21bf24SHyman Huang(黄勇) if (info->has_vcpu_dirty_rate) { 8800e21bf24SHyman Huang(黄勇) DirtyRateVcpuList *rate, *head = info->vcpu_dirty_rate; 8810e21bf24SHyman Huang(黄勇) for (rate = head; rate != NULL; rate = rate->next) { 8820e21bf24SHyman Huang(黄勇) monitor_printf(mon, "vcpu[%"PRIi64"], Dirty rate: %"PRIi64 8830e21bf24SHyman Huang(黄勇) " (MB/s)\n", rate->value->id, 8840e21bf24SHyman Huang(黄勇) rate->value->dirty_rate); 8850e21bf24SHyman Huang(黄勇) } 8860e21bf24SHyman Huang(黄勇) } 887a4a571d9SPeter Xu } else { 888a4a571d9SPeter Xu monitor_printf(mon, "(not ready)\n"); 889a4a571d9SPeter Xu } 8900e21bf24SHyman Huang(黄勇) 8910e21bf24SHyman Huang(黄勇) qapi_free_DirtyRateVcpuList(info->vcpu_dirty_rate); 892a4a571d9SPeter Xu g_free(info); 893a4a571d9SPeter Xu } 894a4a571d9SPeter Xu 895a4a571d9SPeter Xu void hmp_calc_dirty_rate(Monitor *mon, const QDict *qdict) 896a4a571d9SPeter Xu { 897a4a571d9SPeter Xu int64_t sec = qdict_get_try_int(qdict, "second", 0); 898a4a571d9SPeter Xu int64_t sample_pages = qdict_get_try_int(qdict, "sample_pages_per_GB", -1); 899a4a571d9SPeter Xu bool has_sample_pages = (sample_pages != -1); 9000e21bf24SHyman Huang(黄勇) bool dirty_ring = qdict_get_try_bool(qdict, "dirty_ring", false); 901826b8bc8SHyman Huang(黄勇) bool dirty_bitmap = qdict_get_try_bool(qdict, "dirty_bitmap", false); 902826b8bc8SHyman Huang(黄勇) DirtyRateMeasureMode mode = DIRTY_RATE_MEASURE_MODE_PAGE_SAMPLING; 903a4a571d9SPeter Xu Error *err = NULL; 904a4a571d9SPeter Xu 905a4a571d9SPeter Xu if (!sec) { 906a4a571d9SPeter Xu monitor_printf(mon, "Incorrect period length specified!\n"); 907a4a571d9SPeter Xu return; 908a4a571d9SPeter Xu } 909a4a571d9SPeter Xu 910826b8bc8SHyman Huang(黄勇) if (dirty_ring && dirty_bitmap) { 911826b8bc8SHyman Huang(黄勇) monitor_printf(mon, "Either dirty ring or dirty bitmap " 912826b8bc8SHyman Huang(黄勇) "can be specified!\n"); 913826b8bc8SHyman Huang(黄勇) return; 914826b8bc8SHyman Huang(黄勇) } 915826b8bc8SHyman Huang(黄勇) 916826b8bc8SHyman Huang(黄勇) if (dirty_bitmap) { 917826b8bc8SHyman Huang(黄勇) mode = DIRTY_RATE_MEASURE_MODE_DIRTY_BITMAP; 918826b8bc8SHyman Huang(黄勇) } else if (dirty_ring) { 919826b8bc8SHyman Huang(黄勇) mode = DIRTY_RATE_MEASURE_MODE_DIRTY_RING; 920826b8bc8SHyman Huang(黄勇) } 921826b8bc8SHyman Huang(黄勇) 92234a68001SAndrei Gudkov qmp_calc_dirty_rate(sec, /* calc-time */ 92334a68001SAndrei Gudkov false, TIME_UNIT_SECOND, /* calc-time-unit */ 92434a68001SAndrei Gudkov has_sample_pages, sample_pages, 92534a68001SAndrei Gudkov true, mode, 92634a68001SAndrei Gudkov &err); 927a4a571d9SPeter Xu if (err) { 928a4a571d9SPeter Xu hmp_handle_error(mon, err); 929a4a571d9SPeter Xu return; 930a4a571d9SPeter Xu } 931a4a571d9SPeter Xu 932a4a571d9SPeter Xu monitor_printf(mon, "Starting dirty rate measurement with period %"PRIi64 933a4a571d9SPeter Xu " seconds\n", sec); 934a4a571d9SPeter Xu monitor_printf(mon, "[Please use 'info dirty_rate' to check results]\n"); 935a4a571d9SPeter Xu } 936