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"
27a4a571d9SPeter Xu #include "qapi/qmp/qdict.h"
280e21bf24SHyman Huang(黄勇) #include "sysemu/kvm.h"
290e21bf24SHyman Huang(黄勇) #include "sysemu/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
dirty_stat_wait(int64_t msec,int64_t initial_time)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
record_dirtypages(DirtyPageRecord * dirty_pages,CPUState * cpu,bool start)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(黄勇)
do_calculate_dirtyrate(DirtyPageRecord dirty_pages,int64_t calc_time_ms)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(黄勇)
global_dirty_log_change(unsigned int flag,bool start)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(黄勇) */
global_dirty_log_sync(unsigned int flag,bool one_shot)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(黄勇)
vcpu_dirty_stat_alloc(VcpuStat * stat)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(黄勇)
vcpu_dirty_stat_collect(DirtyPageRecord * records,bool start)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(黄勇)
vcpu_calculate_dirtyrate(int64_t calc_time_ms,VcpuStat * stat,unsigned int flag,bool one_shot)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(黄勇)
is_calc_time_valid(int64_t msec)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
is_sample_pages_valid(int64_t pages)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(黄勇)
dirtyrate_set_state(int * state,int old_state,int new_state)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 */
time_unit_to_power(TimeUnit time_unit)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
convert_time_unit(int64_t value,TimeUnit unit_from,TimeUnit unit_to)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 *
query_dirty_rate_info(TimeUnit calc_time_unit)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
init_dirtyrate_stat(struct DirtyRateConfig config)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
cleanup_dirtyrate_stat(struct DirtyRateConfig config)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(黄勇)
update_dirtyrate_stat(struct RamblockDirtyInfo * info)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
update_dirtyrate(uint64_t msec)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 */
compute_page_hash(void * ptr)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 */
get_ramblock_vfn_hash(struct RamblockDirtyInfo * info,uint64_t vfn)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
save_ramblock_hash(struct RamblockDirtyInfo * info)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
get_ramblock_dirty_info(RAMBlock * block,struct RamblockDirtyInfo * info,struct DirtyRateConfig * config)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;
440*88c3b57fSThomas 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);
449*88c3b57fSThomas Huth len = g_strlcpy(info->idstr, qemu_ram_get_idstr(block),
450*88c3b57fSThomas Huth sizeof(info->idstr));
451*88c3b57fSThomas Huth g_assert(len < sizeof(info->idstr));
452ba0e519fSChuan Zheng }
453ba0e519fSChuan Zheng
free_ramblock_dirty_info(struct RamblockDirtyInfo * infos,int count)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
skip_sample_ramblock(RAMBlock * block)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
record_ramblock_hash_info(struct RamblockDirtyInfo ** block_dinfo,struct DirtyRateConfig config,int * block_count)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
calc_page_dirty_rate(struct RamblockDirtyInfo * info)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 *
find_block_matched(RAMBlock * block,int count,struct RamblockDirtyInfo * infos)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
compare_page_hash_info(struct RamblockDirtyInfo * info,int block_count)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
record_dirtypages_bitmap(DirtyPageRecord * dirty_pages,bool start)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(黄勇)
dirtyrate_manual_reset_protect(void)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(黄勇)
calculate_dirtyrate_dirty_bitmap(struct DirtyRateConfig config)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(黄勇)
calculate_dirtyrate_dirty_ring(struct DirtyRateConfig config)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(黄勇)
calculate_dirtyrate_sample_vm(struct DirtyRateConfig config)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
calculate_dirtyrate(struct DirtyRateConfig config)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(黄勇)
get_dirtyrate_thread(void * arg)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
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)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
qmp_query_dirty_rate(bool has_calc_time_unit,TimeUnit calc_time_unit,Error ** errp)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
hmp_info_dirty_rate(Monitor * mon,const QDict * qdict)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
hmp_calc_dirty_rate(Monitor * mon,const QDict * qdict)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