xref: /qemu/migration/dirtyrate.c (revision 195801d700c008b6a8d8acfa299aa5f177446647)
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"
15662770afSMarc-André Lureau #include <zlib.h>
161e05888aSPhilippe Mathieu-Daudé #include "hw/core/cpu.h"
174240dceeSChuan Zheng #include "qapi/error.h"
184240dceeSChuan Zheng #include "exec/ramblock.h"
19beeda9b7SJuan Quintela #include "exec/target_page.h"
204240dceeSChuan Zheng #include "qemu/rcu_queue.h"
210e21bf24SHyman Huang(黄勇) #include "qemu/main-loop.h"
224240dceeSChuan Zheng #include "qapi/qapi-commands-migration.h"
233ded54b1SChuan Zheng #include "ram.h"
243c0b5dffSChuan Zheng #include "trace.h"
254240dceeSChuan Zheng #include "dirtyrate.h"
26a4a571d9SPeter Xu #include "monitor/hmp.h"
27a4a571d9SPeter Xu #include "monitor/monitor.h"
28a4a571d9SPeter Xu #include "qapi/qmp/qdict.h"
290e21bf24SHyman Huang(黄勇) #include "sysemu/kvm.h"
300e21bf24SHyman Huang(黄勇) #include "sysemu/runstate.h"
310e21bf24SHyman Huang(黄勇) #include "exec/memory.h"
3200a3f9c6SAndrei Gudkov #include "qemu/xxhash.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(黄勇) {
93*195801d7SStefan Hajnoczi     bql_lock();
948244166dSHyman Huang(黄勇)     if (start) {
958244166dSHyman Huang(黄勇)         memory_global_dirty_log_start(flag);
968244166dSHyman Huang(黄勇)     } else {
978244166dSHyman Huang(黄勇)         memory_global_dirty_log_stop(flag);
988244166dSHyman Huang(黄勇)     }
99*195801d7SStefan Hajnoczi     bql_unlock();
1008244166dSHyman Huang(黄勇) }
1018244166dSHyman Huang(黄勇) 
1028244166dSHyman Huang(黄勇) /*
1038244166dSHyman Huang(黄勇)  * global_dirty_log_sync
1048244166dSHyman Huang(黄勇)  * 1. sync dirty log from kvm
1058244166dSHyman Huang(黄勇)  * 2. stop dirty tracking if needed.
1068244166dSHyman Huang(黄勇)  */
1078244166dSHyman Huang(黄勇) static void global_dirty_log_sync(unsigned int flag, bool one_shot)
1088244166dSHyman Huang(黄勇) {
109*195801d7SStefan Hajnoczi     bql_lock();
1101e493be5SGavin Shan     memory_global_dirty_log_sync(false);
1118244166dSHyman Huang(黄勇)     if (one_shot) {
1128244166dSHyman Huang(黄勇)         memory_global_dirty_log_stop(flag);
1138244166dSHyman Huang(黄勇)     }
114*195801d7SStefan Hajnoczi     bql_unlock();
1158244166dSHyman Huang(黄勇) }
1168244166dSHyman Huang(黄勇) 
1178244166dSHyman Huang(黄勇) static DirtyPageRecord *vcpu_dirty_stat_alloc(VcpuStat *stat)
1188244166dSHyman Huang(黄勇) {
1198244166dSHyman Huang(黄勇)     CPUState *cpu;
1208244166dSHyman Huang(黄勇)     int nvcpu = 0;
1218244166dSHyman Huang(黄勇) 
1228244166dSHyman Huang(黄勇)     CPU_FOREACH(cpu) {
1238244166dSHyman Huang(黄勇)         nvcpu++;
1248244166dSHyman Huang(黄勇)     }
1258244166dSHyman Huang(黄勇) 
1268244166dSHyman Huang(黄勇)     stat->nvcpu = nvcpu;
127c5e8d518SMarkus Armbruster     stat->rates = g_new0(DirtyRateVcpu, nvcpu);
1288244166dSHyman Huang(黄勇) 
12966997c42SMarkus Armbruster     return g_new0(DirtyPageRecord, nvcpu);
1308244166dSHyman Huang(黄勇) }
1318244166dSHyman Huang(黄勇) 
1324918712fSWafer static void vcpu_dirty_stat_collect(DirtyPageRecord *records,
1338244166dSHyman Huang(黄勇)                                     bool start)
1348244166dSHyman Huang(黄勇) {
1358244166dSHyman Huang(黄勇)     CPUState *cpu;
1368244166dSHyman Huang(黄勇) 
1378244166dSHyman Huang(黄勇)     CPU_FOREACH(cpu) {
1388244166dSHyman Huang(黄勇)         record_dirtypages(records, cpu, start);
1398244166dSHyman Huang(黄勇)     }
1408244166dSHyman Huang(黄勇) }
1418244166dSHyman Huang(黄勇) 
1428244166dSHyman Huang(黄勇) int64_t vcpu_calculate_dirtyrate(int64_t calc_time_ms,
1438244166dSHyman Huang(黄勇)                                  VcpuStat *stat,
1448244166dSHyman Huang(黄勇)                                  unsigned int flag,
1458244166dSHyman Huang(黄勇)                                  bool one_shot)
1468244166dSHyman Huang(黄勇) {
1478244166dSHyman Huang(黄勇)     DirtyPageRecord *records;
1488244166dSHyman Huang(黄勇)     int64_t init_time_ms;
1498244166dSHyman Huang(黄勇)     int64_t duration;
1508244166dSHyman Huang(黄勇)     int64_t dirtyrate;
1518244166dSHyman Huang(黄勇)     int i = 0;
1528244166dSHyman Huang(黄勇)     unsigned int gen_id;
1538244166dSHyman Huang(黄勇) 
1548244166dSHyman Huang(黄勇) retry:
1558244166dSHyman Huang(黄勇)     init_time_ms = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
1568244166dSHyman Huang(黄勇) 
157370ed600SJamie Iles     WITH_QEMU_LOCK_GUARD(&qemu_cpu_list_lock) {
1588244166dSHyman Huang(黄勇)         gen_id = cpu_list_generation_id_get();
1598244166dSHyman Huang(黄勇)         records = vcpu_dirty_stat_alloc(stat);
1604918712fSWafer         vcpu_dirty_stat_collect(records, true);
161370ed600SJamie Iles     }
1628244166dSHyman Huang(黄勇) 
1638244166dSHyman Huang(黄勇)     duration = dirty_stat_wait(calc_time_ms, init_time_ms);
1648244166dSHyman Huang(黄勇) 
1658244166dSHyman Huang(黄勇)     global_dirty_log_sync(flag, one_shot);
1668244166dSHyman Huang(黄勇) 
167370ed600SJamie Iles     WITH_QEMU_LOCK_GUARD(&qemu_cpu_list_lock) {
1688244166dSHyman Huang(黄勇)         if (gen_id != cpu_list_generation_id_get()) {
1698244166dSHyman Huang(黄勇)             g_free(records);
1708244166dSHyman Huang(黄勇)             g_free(stat->rates);
1718244166dSHyman Huang(黄勇)             cpu_list_unlock();
1728244166dSHyman Huang(黄勇)             goto retry;
1738244166dSHyman Huang(黄勇)         }
1744918712fSWafer         vcpu_dirty_stat_collect(records, false);
175370ed600SJamie Iles     }
1768244166dSHyman Huang(黄勇) 
1778244166dSHyman Huang(黄勇)     for (i = 0; i < stat->nvcpu; i++) {
1788244166dSHyman Huang(黄勇)         dirtyrate = do_calculate_dirtyrate(records[i], duration);
1798244166dSHyman Huang(黄勇) 
1808244166dSHyman Huang(黄勇)         stat->rates[i].id = i;
1818244166dSHyman Huang(黄勇)         stat->rates[i].dirty_rate = dirtyrate;
1828244166dSHyman Huang(黄勇) 
1838244166dSHyman Huang(黄勇)         trace_dirtyrate_do_calculate_vcpu(i, dirtyrate);
1848244166dSHyman Huang(黄勇)     }
1858244166dSHyman Huang(黄勇) 
1868244166dSHyman Huang(黄勇)     g_free(records);
1878244166dSHyman Huang(黄勇) 
1888244166dSHyman Huang(黄勇)     return duration;
1898244166dSHyman Huang(黄勇) }
1908244166dSHyman Huang(黄勇) 
19134a68001SAndrei Gudkov static bool is_calc_time_valid(int64_t msec)
192eca58224SChuan Zheng {
19334a68001SAndrei Gudkov     if ((msec < MIN_CALC_TIME_MS) || (msec > MAX_CALC_TIME_MS)) {
194eca58224SChuan Zheng         return false;
195eca58224SChuan Zheng     }
196eca58224SChuan Zheng 
197eca58224SChuan Zheng     return true;
198eca58224SChuan Zheng }
199eca58224SChuan Zheng 
2007afa08cdSHyman Huang(黄勇) static bool is_sample_pages_valid(int64_t pages)
2017afa08cdSHyman Huang(黄勇) {
2027afa08cdSHyman Huang(黄勇)     return pages >= MIN_SAMPLE_PAGE_COUNT &&
2037afa08cdSHyman Huang(黄勇)            pages <= MAX_SAMPLE_PAGE_COUNT;
2047afa08cdSHyman Huang(黄勇) }
2057afa08cdSHyman Huang(黄勇) 
2067df3aa30SChuan Zheng static int dirtyrate_set_state(int *state, int old_state, int new_state)
2077df3aa30SChuan Zheng {
2087df3aa30SChuan Zheng     assert(new_state < DIRTY_RATE_STATUS__MAX);
2093c0b5dffSChuan Zheng     trace_dirtyrate_set_state(DirtyRateStatus_str(new_state));
2107df3aa30SChuan Zheng     if (qatomic_cmpxchg(state, old_state, new_state) == old_state) {
2117df3aa30SChuan Zheng         return 0;
2127df3aa30SChuan Zheng     } else {
2137df3aa30SChuan Zheng         return -1;
2147df3aa30SChuan Zheng     }
2157df3aa30SChuan Zheng }
2167df3aa30SChuan Zheng 
21734a68001SAndrei Gudkov /* Decimal power of given time unit relative to one second */
21834a68001SAndrei Gudkov static int time_unit_to_power(TimeUnit time_unit)
21934a68001SAndrei Gudkov {
22034a68001SAndrei Gudkov     switch (time_unit) {
22134a68001SAndrei Gudkov     case TIME_UNIT_SECOND:
22234a68001SAndrei Gudkov         return 0;
22334a68001SAndrei Gudkov     case TIME_UNIT_MILLISECOND:
22434a68001SAndrei Gudkov         return -3;
22534a68001SAndrei Gudkov     default:
22634a68001SAndrei Gudkov         assert(false); /* unreachable */
22734a68001SAndrei Gudkov         return 0;
22834a68001SAndrei Gudkov     }
22934a68001SAndrei Gudkov }
23034a68001SAndrei Gudkov 
23134a68001SAndrei Gudkov static int64_t convert_time_unit(int64_t value, TimeUnit unit_from,
23234a68001SAndrei Gudkov                                  TimeUnit unit_to)
23334a68001SAndrei Gudkov {
23434a68001SAndrei Gudkov     int power = time_unit_to_power(unit_from) -
23534a68001SAndrei Gudkov                 time_unit_to_power(unit_to);
23634a68001SAndrei Gudkov     while (power < 0) {
23734a68001SAndrei Gudkov         value /= 10;
23834a68001SAndrei Gudkov         power += 1;
23934a68001SAndrei Gudkov     }
24034a68001SAndrei Gudkov     while (power > 0) {
24134a68001SAndrei Gudkov         value *= 10;
24234a68001SAndrei Gudkov         power -= 1;
24334a68001SAndrei Gudkov     }
24434a68001SAndrei Gudkov     return value;
24534a68001SAndrei Gudkov }
24634a68001SAndrei Gudkov 
24734a68001SAndrei Gudkov 
24834a68001SAndrei Gudkov static struct DirtyRateInfo *
24934a68001SAndrei Gudkov query_dirty_rate_info(TimeUnit calc_time_unit)
2504c437254SChuan Zheng {
2510e21bf24SHyman Huang(黄勇)     int i;
2524c437254SChuan Zheng     int64_t dirty_rate = DirtyStat.dirty_rate;
253b21e2380SMarkus Armbruster     struct DirtyRateInfo *info = g_new0(DirtyRateInfo, 1);
2540e21bf24SHyman Huang(黄勇)     DirtyRateVcpuList *head = NULL, **tail = &head;
2554c437254SChuan Zheng 
2564c437254SChuan Zheng     info->status = CalculatingState;
2574c437254SChuan Zheng     info->start_time = DirtyStat.start_time;
25834a68001SAndrei Gudkov     info->calc_time = convert_time_unit(DirtyStat.calc_time_ms,
25934a68001SAndrei Gudkov                                         TIME_UNIT_MILLISECOND,
26034a68001SAndrei Gudkov                                         calc_time_unit);
26134a68001SAndrei Gudkov     info->calc_time_unit = calc_time_unit;
2627afa08cdSHyman Huang(黄勇)     info->sample_pages = DirtyStat.sample_pages;
2630e21bf24SHyman Huang(黄勇)     info->mode = dirtyrate_mode;
2640e21bf24SHyman Huang(黄勇) 
2650e21bf24SHyman Huang(黄勇)     if (qatomic_read(&CalculatingState) == DIRTY_RATE_STATUS_MEASURED) {
2660e21bf24SHyman Huang(黄勇)         info->has_dirty_rate = true;
2670e21bf24SHyman Huang(黄勇)         info->dirty_rate = dirty_rate;
2680e21bf24SHyman Huang(黄勇) 
2690e21bf24SHyman Huang(黄勇)         if (dirtyrate_mode == DIRTY_RATE_MEASURE_MODE_DIRTY_RING) {
2700e21bf24SHyman Huang(黄勇)             /*
2710e21bf24SHyman Huang(黄勇)              * set sample_pages with 0 to indicate page sampling
2720e21bf24SHyman Huang(黄勇)              * isn't enabled
2730e21bf24SHyman Huang(黄勇)              **/
2740e21bf24SHyman Huang(黄勇)             info->sample_pages = 0;
2750e21bf24SHyman Huang(黄勇)             info->has_vcpu_dirty_rate = true;
2760e21bf24SHyman Huang(黄勇)             for (i = 0; i < DirtyStat.dirty_ring.nvcpu; i++) {
277b21e2380SMarkus Armbruster                 DirtyRateVcpu *rate = g_new0(DirtyRateVcpu, 1);
2780e21bf24SHyman Huang(黄勇)                 rate->id = DirtyStat.dirty_ring.rates[i].id;
2790e21bf24SHyman Huang(黄勇)                 rate->dirty_rate = DirtyStat.dirty_ring.rates[i].dirty_rate;
2800e21bf24SHyman Huang(黄勇)                 QAPI_LIST_APPEND(tail, rate);
2810e21bf24SHyman Huang(黄勇)             }
2820e21bf24SHyman Huang(黄勇)             info->vcpu_dirty_rate = head;
2830e21bf24SHyman Huang(黄勇)         }
284826b8bc8SHyman Huang(黄勇) 
285826b8bc8SHyman Huang(黄勇)         if (dirtyrate_mode == DIRTY_RATE_MEASURE_MODE_DIRTY_BITMAP) {
286826b8bc8SHyman Huang(黄勇)             info->sample_pages = 0;
287826b8bc8SHyman Huang(黄勇)         }
2880e21bf24SHyman Huang(黄勇)     }
2894c437254SChuan Zheng 
2903c0b5dffSChuan Zheng     trace_query_dirty_rate_info(DirtyRateStatus_str(CalculatingState));
2913c0b5dffSChuan Zheng 
2924c437254SChuan Zheng     return info;
2934c437254SChuan Zheng }
2944c437254SChuan Zheng 
295320a6cccSAndrei Gudkov static void init_dirtyrate_stat(struct DirtyRateConfig config)
296c9a58d71SChuan Zheng {
297c9a58d71SChuan Zheng     DirtyStat.dirty_rate = -1;
298320a6cccSAndrei Gudkov     DirtyStat.start_time = qemu_clock_get_ms(QEMU_CLOCK_HOST) / 1000;
29934a68001SAndrei Gudkov     DirtyStat.calc_time_ms = config.calc_time_ms;
30071864eadSHyman Huang(黄勇)     DirtyStat.sample_pages = config.sample_pages_per_gigabytes;
30171864eadSHyman Huang(黄勇) 
30271864eadSHyman Huang(黄勇)     switch (config.mode) {
30371864eadSHyman Huang(黄勇)     case DIRTY_RATE_MEASURE_MODE_PAGE_SAMPLING:
30471864eadSHyman Huang(黄勇)         DirtyStat.page_sampling.total_dirty_samples = 0;
30571864eadSHyman Huang(黄勇)         DirtyStat.page_sampling.total_sample_count = 0;
30671864eadSHyman Huang(黄勇)         DirtyStat.page_sampling.total_block_mem_MB = 0;
30771864eadSHyman Huang(黄勇)         break;
30871864eadSHyman Huang(黄勇)     case DIRTY_RATE_MEASURE_MODE_DIRTY_RING:
30971864eadSHyman Huang(黄勇)         DirtyStat.dirty_ring.nvcpu = -1;
31071864eadSHyman Huang(黄勇)         DirtyStat.dirty_ring.rates = NULL;
31171864eadSHyman Huang(黄勇)         break;
31271864eadSHyman Huang(黄勇)     default:
31371864eadSHyman Huang(黄勇)         break;
31471864eadSHyman Huang(黄勇)     }
315c9a58d71SChuan Zheng }
316c9a58d71SChuan Zheng 
3170e21bf24SHyman Huang(黄勇) static void cleanup_dirtyrate_stat(struct DirtyRateConfig config)
3180e21bf24SHyman Huang(黄勇) {
3190e21bf24SHyman Huang(黄勇)     /* last calc-dirty-rate qmp use dirty ring mode */
3200e21bf24SHyman Huang(黄勇)     if (dirtyrate_mode == DIRTY_RATE_MEASURE_MODE_DIRTY_RING) {
3210e21bf24SHyman Huang(黄勇)         free(DirtyStat.dirty_ring.rates);
3220e21bf24SHyman Huang(黄勇)         DirtyStat.dirty_ring.rates = NULL;
3230e21bf24SHyman Huang(黄勇)     }
3240e21bf24SHyman Huang(黄勇) }
3250e21bf24SHyman Huang(黄勇) 
326c9a58d71SChuan Zheng static void update_dirtyrate_stat(struct RamblockDirtyInfo *info)
327c9a58d71SChuan Zheng {
32871864eadSHyman Huang(黄勇)     DirtyStat.page_sampling.total_dirty_samples += info->sample_dirty_count;
32971864eadSHyman Huang(黄勇)     DirtyStat.page_sampling.total_sample_count += info->sample_pages_count;
330c9a58d71SChuan Zheng     /* size of total pages in MB */
331beeda9b7SJuan Quintela     DirtyStat.page_sampling.total_block_mem_MB +=
332beeda9b7SJuan Quintela         qemu_target_pages_to_MiB(info->ramblock_pages);
333c9a58d71SChuan Zheng }
334c9a58d71SChuan Zheng 
335c9a58d71SChuan Zheng static void update_dirtyrate(uint64_t msec)
336c9a58d71SChuan Zheng {
337c9a58d71SChuan Zheng     uint64_t dirtyrate;
33871864eadSHyman Huang(黄勇)     uint64_t total_dirty_samples = DirtyStat.page_sampling.total_dirty_samples;
33971864eadSHyman Huang(黄勇)     uint64_t total_sample_count = DirtyStat.page_sampling.total_sample_count;
34071864eadSHyman Huang(黄勇)     uint64_t total_block_mem_MB = DirtyStat.page_sampling.total_block_mem_MB;
341c9a58d71SChuan Zheng 
342c9a58d71SChuan Zheng     dirtyrate = total_dirty_samples * total_block_mem_MB *
343c9a58d71SChuan Zheng                 1000 / (total_sample_count * msec);
344c9a58d71SChuan Zheng 
345c9a58d71SChuan Zheng     DirtyStat.dirty_rate = dirtyrate;
346c9a58d71SChuan Zheng }
3477df3aa30SChuan Zheng 
348ba0e519fSChuan Zheng /*
34900a3f9c6SAndrei Gudkov  * Compute hash of a single page of size TARGET_PAGE_SIZE.
35000a3f9c6SAndrei Gudkov  */
35100a3f9c6SAndrei Gudkov static uint32_t compute_page_hash(void *ptr)
35200a3f9c6SAndrei Gudkov {
353edd83a70SJuan Quintela     size_t page_size = qemu_target_page_size();
35400a3f9c6SAndrei Gudkov     uint32_t i;
35500a3f9c6SAndrei Gudkov     uint64_t v1, v2, v3, v4;
35600a3f9c6SAndrei Gudkov     uint64_t res;
35700a3f9c6SAndrei Gudkov     const uint64_t *p = ptr;
35800a3f9c6SAndrei Gudkov 
35900a3f9c6SAndrei Gudkov     v1 = QEMU_XXHASH_SEED + XXH_PRIME64_1 + XXH_PRIME64_2;
36000a3f9c6SAndrei Gudkov     v2 = QEMU_XXHASH_SEED + XXH_PRIME64_2;
36100a3f9c6SAndrei Gudkov     v3 = QEMU_XXHASH_SEED + 0;
36200a3f9c6SAndrei Gudkov     v4 = QEMU_XXHASH_SEED - XXH_PRIME64_1;
363edd83a70SJuan Quintela     for (i = 0; i < page_size / 8; i += 4) {
36400a3f9c6SAndrei Gudkov         v1 = XXH64_round(v1, p[i + 0]);
36500a3f9c6SAndrei Gudkov         v2 = XXH64_round(v2, p[i + 1]);
36600a3f9c6SAndrei Gudkov         v3 = XXH64_round(v3, p[i + 2]);
36700a3f9c6SAndrei Gudkov         v4 = XXH64_round(v4, p[i + 3]);
36800a3f9c6SAndrei Gudkov     }
36900a3f9c6SAndrei Gudkov     res = XXH64_mergerounds(v1, v2, v3, v4);
370edd83a70SJuan Quintela     res += page_size;
37100a3f9c6SAndrei Gudkov     res = XXH64_avalanche(res);
37200a3f9c6SAndrei Gudkov     return (uint32_t)(res & UINT32_MAX);
37300a3f9c6SAndrei Gudkov }
37400a3f9c6SAndrei Gudkov 
37500a3f9c6SAndrei Gudkov 
37600a3f9c6SAndrei Gudkov /*
377ba0e519fSChuan Zheng  * get hash result for the sampled memory with length of TARGET_PAGE_SIZE
378ba0e519fSChuan Zheng  * in ramblock, which starts from ramblock base address.
379ba0e519fSChuan Zheng  */
380ba0e519fSChuan Zheng static uint32_t get_ramblock_vfn_hash(struct RamblockDirtyInfo *info,
381ba0e519fSChuan Zheng                                       uint64_t vfn)
382ba0e519fSChuan Zheng {
38300a3f9c6SAndrei Gudkov     uint32_t hash;
384ba0e519fSChuan Zheng 
385edd83a70SJuan Quintela     hash = compute_page_hash(info->ramblock_addr +
386edd83a70SJuan Quintela                              vfn * qemu_target_page_size());
387ba0e519fSChuan Zheng 
38800a3f9c6SAndrei Gudkov     trace_get_ramblock_vfn_hash(info->idstr, vfn, hash);
38900a3f9c6SAndrei Gudkov     return hash;
390ba0e519fSChuan Zheng }
391ba0e519fSChuan Zheng 
392ba0e519fSChuan Zheng static bool save_ramblock_hash(struct RamblockDirtyInfo *info)
393ba0e519fSChuan Zheng {
394ba0e519fSChuan Zheng     unsigned int sample_pages_count;
395ba0e519fSChuan Zheng     int i;
396ba0e519fSChuan Zheng     GRand *rand;
397ba0e519fSChuan Zheng 
398ba0e519fSChuan Zheng     sample_pages_count = info->sample_pages_count;
399ba0e519fSChuan Zheng 
400ba0e519fSChuan Zheng     /* ramblock size less than one page, return success to skip this ramblock */
401ba0e519fSChuan Zheng     if (unlikely(info->ramblock_pages == 0 || sample_pages_count == 0)) {
402ba0e519fSChuan Zheng         return true;
403ba0e519fSChuan Zheng     }
404ba0e519fSChuan Zheng 
405ba0e519fSChuan Zheng     info->hash_result = g_try_malloc0_n(sample_pages_count,
406ba0e519fSChuan Zheng                                         sizeof(uint32_t));
407ba0e519fSChuan Zheng     if (!info->hash_result) {
408ba0e519fSChuan Zheng         return false;
409ba0e519fSChuan Zheng     }
410ba0e519fSChuan Zheng 
411ba0e519fSChuan Zheng     info->sample_page_vfn = g_try_malloc0_n(sample_pages_count,
412ba0e519fSChuan Zheng                                             sizeof(uint64_t));
413ba0e519fSChuan Zheng     if (!info->sample_page_vfn) {
414ba0e519fSChuan Zheng         g_free(info->hash_result);
415ba0e519fSChuan Zheng         return false;
416ba0e519fSChuan Zheng     }
417ba0e519fSChuan Zheng 
418ba0e519fSChuan Zheng     rand  = g_rand_new();
419ba0e519fSChuan Zheng     for (i = 0; i < sample_pages_count; i++) {
420ba0e519fSChuan Zheng         info->sample_page_vfn[i] = g_rand_int_range(rand, 0,
421ba0e519fSChuan Zheng                                                     info->ramblock_pages - 1);
422ba0e519fSChuan Zheng         info->hash_result[i] = get_ramblock_vfn_hash(info,
423ba0e519fSChuan Zheng                                                      info->sample_page_vfn[i]);
424ba0e519fSChuan Zheng     }
425ba0e519fSChuan Zheng     g_rand_free(rand);
426ba0e519fSChuan Zheng 
427ba0e519fSChuan Zheng     return true;
428ba0e519fSChuan Zheng }
429ba0e519fSChuan Zheng 
430ba0e519fSChuan Zheng static void get_ramblock_dirty_info(RAMBlock *block,
431ba0e519fSChuan Zheng                                     struct RamblockDirtyInfo *info,
432ba0e519fSChuan Zheng                                     struct DirtyRateConfig *config)
433ba0e519fSChuan Zheng {
434ba0e519fSChuan Zheng     uint64_t sample_pages_per_gigabytes = config->sample_pages_per_gigabytes;
435ba0e519fSChuan Zheng 
436ba0e519fSChuan Zheng     /* Right shift 30 bits to calc ramblock size in GB */
437ba0e519fSChuan Zheng     info->sample_pages_count = (qemu_ram_get_used_length(block) *
438ba0e519fSChuan Zheng                                 sample_pages_per_gigabytes) >> 30;
439ba0e519fSChuan Zheng     /* Right shift TARGET_PAGE_BITS to calc page count */
440ba0e519fSChuan Zheng     info->ramblock_pages = qemu_ram_get_used_length(block) >>
441148b1ad8SJuan Quintela                            qemu_target_page_bits();
442ba0e519fSChuan Zheng     info->ramblock_addr = qemu_ram_get_host_addr(block);
443ba0e519fSChuan Zheng     strcpy(info->idstr, qemu_ram_get_idstr(block));
444ba0e519fSChuan Zheng }
445ba0e519fSChuan Zheng 
446cf0bbb49SChuan Zheng static void free_ramblock_dirty_info(struct RamblockDirtyInfo *infos, int count)
447cf0bbb49SChuan Zheng {
448cf0bbb49SChuan Zheng     int i;
449cf0bbb49SChuan Zheng 
450cf0bbb49SChuan Zheng     if (!infos) {
451cf0bbb49SChuan Zheng         return;
452cf0bbb49SChuan Zheng     }
453cf0bbb49SChuan Zheng 
454cf0bbb49SChuan Zheng     for (i = 0; i < count; i++) {
455cf0bbb49SChuan Zheng         g_free(infos[i].sample_page_vfn);
456cf0bbb49SChuan Zheng         g_free(infos[i].hash_result);
457cf0bbb49SChuan Zheng     }
458cf0bbb49SChuan Zheng     g_free(infos);
459cf0bbb49SChuan Zheng }
460cf0bbb49SChuan Zheng 
461f82583cdSChuan Zheng static bool skip_sample_ramblock(RAMBlock *block)
462f82583cdSChuan Zheng {
463f82583cdSChuan Zheng     /*
464f82583cdSChuan Zheng      * Sample only blocks larger than MIN_RAMBLOCK_SIZE.
465f82583cdSChuan Zheng      */
466f82583cdSChuan Zheng     if (qemu_ram_get_used_length(block) < (MIN_RAMBLOCK_SIZE << 10)) {
4673c0b5dffSChuan Zheng         trace_skip_sample_ramblock(block->idstr,
4683c0b5dffSChuan Zheng                                    qemu_ram_get_used_length(block));
469f82583cdSChuan Zheng         return true;
470f82583cdSChuan Zheng     }
471f82583cdSChuan Zheng 
472f82583cdSChuan Zheng     return false;
473f82583cdSChuan Zheng }
474f82583cdSChuan Zheng 
475ba0e519fSChuan Zheng static bool record_ramblock_hash_info(struct RamblockDirtyInfo **block_dinfo,
476ba0e519fSChuan Zheng                                       struct DirtyRateConfig config,
477ba0e519fSChuan Zheng                                       int *block_count)
478ba0e519fSChuan Zheng {
479ba0e519fSChuan Zheng     struct RamblockDirtyInfo *info = NULL;
480ba0e519fSChuan Zheng     struct RamblockDirtyInfo *dinfo = NULL;
481ba0e519fSChuan Zheng     RAMBlock *block = NULL;
482ba0e519fSChuan Zheng     int total_count = 0;
483ba0e519fSChuan Zheng     int index = 0;
484ba0e519fSChuan Zheng     bool ret = false;
485ba0e519fSChuan Zheng 
486ba0e519fSChuan Zheng     RAMBLOCK_FOREACH_MIGRATABLE(block) {
487f82583cdSChuan Zheng         if (skip_sample_ramblock(block)) {
488f82583cdSChuan Zheng             continue;
489f82583cdSChuan Zheng         }
490ba0e519fSChuan Zheng         total_count++;
491ba0e519fSChuan Zheng     }
492ba0e519fSChuan Zheng 
493ba0e519fSChuan Zheng     dinfo = g_try_malloc0_n(total_count, sizeof(struct RamblockDirtyInfo));
494ba0e519fSChuan Zheng     if (dinfo == NULL) {
495ba0e519fSChuan Zheng         goto out;
496ba0e519fSChuan Zheng     }
497ba0e519fSChuan Zheng 
498ba0e519fSChuan Zheng     RAMBLOCK_FOREACH_MIGRATABLE(block) {
499f82583cdSChuan Zheng         if (skip_sample_ramblock(block)) {
500f82583cdSChuan Zheng             continue;
501f82583cdSChuan Zheng         }
502ba0e519fSChuan Zheng         if (index >= total_count) {
503ba0e519fSChuan Zheng             break;
504ba0e519fSChuan Zheng         }
505ba0e519fSChuan Zheng         info = &dinfo[index];
506ba0e519fSChuan Zheng         get_ramblock_dirty_info(block, info, &config);
507ba0e519fSChuan Zheng         if (!save_ramblock_hash(info)) {
508ba0e519fSChuan Zheng             goto out;
509ba0e519fSChuan Zheng         }
510ba0e519fSChuan Zheng         index++;
511ba0e519fSChuan Zheng     }
512ba0e519fSChuan Zheng     ret = true;
513ba0e519fSChuan Zheng 
514ba0e519fSChuan Zheng out:
515ba0e519fSChuan Zheng     *block_count = index;
516ba0e519fSChuan Zheng     *block_dinfo = dinfo;
517ba0e519fSChuan Zheng     return ret;
518ba0e519fSChuan Zheng }
519ba0e519fSChuan Zheng 
5209c04387bSChuan Zheng static void calc_page_dirty_rate(struct RamblockDirtyInfo *info)
5219c04387bSChuan Zheng {
52200a3f9c6SAndrei Gudkov     uint32_t hash;
5239c04387bSChuan Zheng     int i;
5249c04387bSChuan Zheng 
5259c04387bSChuan Zheng     for (i = 0; i < info->sample_pages_count; i++) {
52600a3f9c6SAndrei Gudkov         hash = get_ramblock_vfn_hash(info, info->sample_page_vfn[i]);
52700a3f9c6SAndrei Gudkov         if (hash != info->hash_result[i]) {
52800a3f9c6SAndrei Gudkov             trace_calc_page_dirty_rate(info->idstr, hash, info->hash_result[i]);
5299c04387bSChuan Zheng             info->sample_dirty_count++;
5309c04387bSChuan Zheng         }
5319c04387bSChuan Zheng     }
5329c04387bSChuan Zheng }
5339c04387bSChuan Zheng 
5349c04387bSChuan Zheng static struct RamblockDirtyInfo *
5359c04387bSChuan Zheng find_block_matched(RAMBlock *block, int count,
5369c04387bSChuan Zheng                   struct RamblockDirtyInfo *infos)
5379c04387bSChuan Zheng {
5389c04387bSChuan Zheng     int i;
5399c04387bSChuan Zheng 
5409c04387bSChuan Zheng     for (i = 0; i < count; i++) {
5419c04387bSChuan Zheng         if (!strcmp(infos[i].idstr, qemu_ram_get_idstr(block))) {
5429c04387bSChuan Zheng             break;
5439c04387bSChuan Zheng         }
5449c04387bSChuan Zheng     }
5459c04387bSChuan Zheng 
5469c04387bSChuan Zheng     if (i == count) {
5479c04387bSChuan Zheng         return NULL;
5489c04387bSChuan Zheng     }
5499c04387bSChuan Zheng 
5509c04387bSChuan Zheng     if (infos[i].ramblock_addr != qemu_ram_get_host_addr(block) ||
5519c04387bSChuan Zheng         infos[i].ramblock_pages !=
552148b1ad8SJuan Quintela             (qemu_ram_get_used_length(block) >> qemu_target_page_bits())) {
5533c0b5dffSChuan Zheng         trace_find_page_matched(block->idstr);
5549c04387bSChuan Zheng         return NULL;
5559c04387bSChuan Zheng     }
5569c04387bSChuan Zheng 
55766997c42SMarkus Armbruster     return &infos[i];
5589c04387bSChuan Zheng }
5599c04387bSChuan Zheng 
5609c04387bSChuan Zheng static bool compare_page_hash_info(struct RamblockDirtyInfo *info,
5619c04387bSChuan Zheng                                   int block_count)
5629c04387bSChuan Zheng {
5639c04387bSChuan Zheng     struct RamblockDirtyInfo *block_dinfo = NULL;
5649c04387bSChuan Zheng     RAMBlock *block = NULL;
5659c04387bSChuan Zheng 
5669c04387bSChuan Zheng     RAMBLOCK_FOREACH_MIGRATABLE(block) {
567f82583cdSChuan Zheng         if (skip_sample_ramblock(block)) {
568f82583cdSChuan Zheng             continue;
569f82583cdSChuan Zheng         }
5709c04387bSChuan Zheng         block_dinfo = find_block_matched(block, block_count, info);
5719c04387bSChuan Zheng         if (block_dinfo == NULL) {
5729c04387bSChuan Zheng             continue;
5739c04387bSChuan Zheng         }
5749c04387bSChuan Zheng         calc_page_dirty_rate(block_dinfo);
5759c04387bSChuan Zheng         update_dirtyrate_stat(block_dinfo);
5769c04387bSChuan Zheng     }
5779c04387bSChuan Zheng 
57871864eadSHyman Huang(黄勇)     if (DirtyStat.page_sampling.total_sample_count == 0) {
5799c04387bSChuan Zheng         return false;
5809c04387bSChuan Zheng     }
5819c04387bSChuan Zheng 
5829c04387bSChuan Zheng     return true;
5839c04387bSChuan Zheng }
5849c04387bSChuan Zheng 
585826b8bc8SHyman Huang(黄勇) static inline void record_dirtypages_bitmap(DirtyPageRecord *dirty_pages,
586826b8bc8SHyman Huang(黄勇)                                             bool start)
587826b8bc8SHyman Huang(黄勇) {
588826b8bc8SHyman Huang(黄勇)     if (start) {
589826b8bc8SHyman Huang(黄勇)         dirty_pages->start_pages = total_dirty_pages;
590826b8bc8SHyman Huang(黄勇)     } else {
591826b8bc8SHyman Huang(黄勇)         dirty_pages->end_pages = total_dirty_pages;
592826b8bc8SHyman Huang(黄勇)     }
593826b8bc8SHyman Huang(黄勇) }
594826b8bc8SHyman Huang(黄勇) 
595826b8bc8SHyman Huang(黄勇) static inline void dirtyrate_manual_reset_protect(void)
596826b8bc8SHyman Huang(黄勇) {
597826b8bc8SHyman Huang(黄勇)     RAMBlock *block = NULL;
598826b8bc8SHyman Huang(黄勇) 
599826b8bc8SHyman Huang(黄勇)     WITH_RCU_READ_LOCK_GUARD() {
600826b8bc8SHyman Huang(黄勇)         RAMBLOCK_FOREACH_MIGRATABLE(block) {
601826b8bc8SHyman Huang(黄勇)             memory_region_clear_dirty_bitmap(block->mr, 0,
602826b8bc8SHyman Huang(黄勇)                                              block->used_length);
603826b8bc8SHyman Huang(黄勇)         }
604826b8bc8SHyman Huang(黄勇)     }
605826b8bc8SHyman Huang(黄勇) }
606826b8bc8SHyman Huang(黄勇) 
607826b8bc8SHyman Huang(黄勇) static void calculate_dirtyrate_dirty_bitmap(struct DirtyRateConfig config)
608826b8bc8SHyman Huang(黄勇) {
609826b8bc8SHyman Huang(黄勇)     int64_t start_time;
610826b8bc8SHyman Huang(黄勇)     DirtyPageRecord dirty_pages;
611826b8bc8SHyman Huang(黄勇) 
612*195801d7SStefan Hajnoczi     bql_lock();
613826b8bc8SHyman Huang(黄勇)     memory_global_dirty_log_start(GLOBAL_DIRTY_DIRTY_RATE);
614826b8bc8SHyman Huang(黄勇) 
615826b8bc8SHyman Huang(黄勇)     /*
616826b8bc8SHyman Huang(黄勇)      * 1'round of log sync may return all 1 bits with
617826b8bc8SHyman Huang(黄勇)      * KVM_DIRTY_LOG_INITIALLY_SET enable
618826b8bc8SHyman Huang(黄勇)      * skip it unconditionally and start dirty tracking
619826b8bc8SHyman Huang(黄勇)      * from 2'round of log sync
620826b8bc8SHyman Huang(黄勇)      */
6211e493be5SGavin Shan     memory_global_dirty_log_sync(false);
622826b8bc8SHyman Huang(黄勇) 
623826b8bc8SHyman Huang(黄勇)     /*
624826b8bc8SHyman Huang(黄勇)      * reset page protect manually and unconditionally.
625826b8bc8SHyman Huang(黄勇)      * this make sure kvm dirty log be cleared if
626826b8bc8SHyman Huang(黄勇)      * KVM_DIRTY_LOG_MANUAL_PROTECT_ENABLE cap is enabled.
627826b8bc8SHyman Huang(黄勇)      */
628826b8bc8SHyman Huang(黄勇)     dirtyrate_manual_reset_protect();
629*195801d7SStefan Hajnoczi     bql_unlock();
630826b8bc8SHyman Huang(黄勇) 
631826b8bc8SHyman Huang(黄勇)     record_dirtypages_bitmap(&dirty_pages, true);
632826b8bc8SHyman Huang(黄勇) 
633826b8bc8SHyman Huang(黄勇)     start_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
634320a6cccSAndrei Gudkov     DirtyStat.start_time = qemu_clock_get_ms(QEMU_CLOCK_HOST) / 1000;
635826b8bc8SHyman Huang(黄勇) 
63634a68001SAndrei Gudkov     DirtyStat.calc_time_ms = dirty_stat_wait(config.calc_time_ms, start_time);
637826b8bc8SHyman Huang(黄勇) 
638826b8bc8SHyman Huang(黄勇)     /*
6398244166dSHyman Huang(黄勇)      * do two things.
640826b8bc8SHyman Huang(黄勇)      * 1. fetch dirty bitmap from kvm
641826b8bc8SHyman Huang(黄勇)      * 2. stop dirty tracking
642826b8bc8SHyman Huang(黄勇)      */
6438244166dSHyman Huang(黄勇)     global_dirty_log_sync(GLOBAL_DIRTY_DIRTY_RATE, true);
644826b8bc8SHyman Huang(黄勇) 
645826b8bc8SHyman Huang(黄勇)     record_dirtypages_bitmap(&dirty_pages, false);
646826b8bc8SHyman Huang(黄勇) 
64734a68001SAndrei Gudkov     DirtyStat.dirty_rate = do_calculate_dirtyrate(dirty_pages,
64834a68001SAndrei Gudkov                                                   DirtyStat.calc_time_ms);
649826b8bc8SHyman Huang(黄勇) }
650826b8bc8SHyman Huang(黄勇) 
6510e21bf24SHyman Huang(黄勇) static void calculate_dirtyrate_dirty_ring(struct DirtyRateConfig config)
6520e21bf24SHyman Huang(黄勇) {
6530e21bf24SHyman Huang(黄勇)     uint64_t dirtyrate = 0;
6540e21bf24SHyman Huang(黄勇)     uint64_t dirtyrate_sum = 0;
6550e21bf24SHyman Huang(黄勇)     int i = 0;
6560e21bf24SHyman Huang(黄勇) 
6578244166dSHyman Huang(黄勇)     /* start log sync */
6588244166dSHyman Huang(黄勇)     global_dirty_log_change(GLOBAL_DIRTY_DIRTY_RATE, true);
6590e21bf24SHyman Huang(黄勇) 
660320a6cccSAndrei Gudkov     DirtyStat.start_time = qemu_clock_get_ms(QEMU_CLOCK_HOST) / 1000;
6610e21bf24SHyman Huang(黄勇) 
6628244166dSHyman Huang(黄勇)     /* calculate vcpu dirtyrate */
66334a68001SAndrei Gudkov     DirtyStat.calc_time_ms = vcpu_calculate_dirtyrate(config.calc_time_ms,
6648244166dSHyman Huang(黄勇)                                                       &DirtyStat.dirty_ring,
6658244166dSHyman Huang(黄勇)                                                       GLOBAL_DIRTY_DIRTY_RATE,
6668244166dSHyman Huang(黄勇)                                                       true);
6670e21bf24SHyman Huang(黄勇) 
6688244166dSHyman Huang(黄勇)     /* calculate vm dirtyrate */
6690e21bf24SHyman Huang(黄勇)     for (i = 0; i < DirtyStat.dirty_ring.nvcpu; i++) {
6708244166dSHyman Huang(黄勇)         dirtyrate = DirtyStat.dirty_ring.rates[i].dirty_rate;
6710e21bf24SHyman Huang(黄勇)         DirtyStat.dirty_ring.rates[i].dirty_rate = dirtyrate;
6720e21bf24SHyman Huang(黄勇)         dirtyrate_sum += dirtyrate;
6730e21bf24SHyman Huang(黄勇)     }
6740e21bf24SHyman Huang(黄勇) 
6750e21bf24SHyman Huang(黄勇)     DirtyStat.dirty_rate = dirtyrate_sum;
6760e21bf24SHyman Huang(黄勇) }
6770e21bf24SHyman Huang(黄勇) 
6780e21bf24SHyman Huang(黄勇) static void calculate_dirtyrate_sample_vm(struct DirtyRateConfig config)
6794240dceeSChuan Zheng {
680cf0bbb49SChuan Zheng     struct RamblockDirtyInfo *block_dinfo = NULL;
681cf0bbb49SChuan Zheng     int block_count = 0;
682cf0bbb49SChuan Zheng     int64_t initial_time;
683cf0bbb49SChuan Zheng 
684cf0bbb49SChuan Zheng     rcu_read_lock();
685cf0bbb49SChuan Zheng     initial_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
686320a6cccSAndrei Gudkov     DirtyStat.start_time = qemu_clock_get_ms(QEMU_CLOCK_HOST) / 1000;
687cf0bbb49SChuan Zheng     if (!record_ramblock_hash_info(&block_dinfo, config, &block_count)) {
688cf0bbb49SChuan Zheng         goto out;
689cf0bbb49SChuan Zheng     }
690cf0bbb49SChuan Zheng     rcu_read_unlock();
691cf0bbb49SChuan Zheng 
69234a68001SAndrei Gudkov     DirtyStat.calc_time_ms = dirty_stat_wait(config.calc_time_ms,
69334a68001SAndrei Gudkov                                              initial_time);
694cf0bbb49SChuan Zheng 
695cf0bbb49SChuan Zheng     rcu_read_lock();
696cf0bbb49SChuan Zheng     if (!compare_page_hash_info(block_dinfo, block_count)) {
697cf0bbb49SChuan Zheng         goto out;
698cf0bbb49SChuan Zheng     }
699cf0bbb49SChuan Zheng 
70034a68001SAndrei Gudkov     update_dirtyrate(DirtyStat.calc_time_ms);
701cf0bbb49SChuan Zheng 
702cf0bbb49SChuan Zheng out:
703cf0bbb49SChuan Zheng     rcu_read_unlock();
704cf0bbb49SChuan Zheng     free_ramblock_dirty_info(block_dinfo, block_count);
7054240dceeSChuan Zheng }
7064240dceeSChuan Zheng 
7070e21bf24SHyman Huang(黄勇) static void calculate_dirtyrate(struct DirtyRateConfig config)
7080e21bf24SHyman Huang(黄勇) {
709826b8bc8SHyman Huang(黄勇)     if (config.mode == DIRTY_RATE_MEASURE_MODE_DIRTY_BITMAP) {
710826b8bc8SHyman Huang(黄勇)         calculate_dirtyrate_dirty_bitmap(config);
711826b8bc8SHyman Huang(黄勇)     } else if (config.mode == DIRTY_RATE_MEASURE_MODE_DIRTY_RING) {
7120e21bf24SHyman Huang(黄勇)         calculate_dirtyrate_dirty_ring(config);
7130e21bf24SHyman Huang(黄勇)     } else {
7140e21bf24SHyman Huang(黄勇)         calculate_dirtyrate_sample_vm(config);
7150e21bf24SHyman Huang(黄勇)     }
7160e21bf24SHyman Huang(黄勇) 
7170e21bf24SHyman Huang(黄勇)     trace_dirtyrate_calculate(DirtyStat.dirty_rate);
7180e21bf24SHyman Huang(黄勇) }
7190e21bf24SHyman Huang(黄勇) 
7204240dceeSChuan Zheng void *get_dirtyrate_thread(void *arg)
7214240dceeSChuan Zheng {
7224240dceeSChuan Zheng     struct DirtyRateConfig config = *(struct DirtyRateConfig *)arg;
7237df3aa30SChuan Zheng     int ret;
72415eb2d64SHyman Huang(黄勇)     rcu_register_thread();
7257df3aa30SChuan Zheng 
7267df3aa30SChuan Zheng     ret = dirtyrate_set_state(&CalculatingState, DIRTY_RATE_STATUS_UNSTARTED,
7277df3aa30SChuan Zheng                               DIRTY_RATE_STATUS_MEASURING);
7287df3aa30SChuan Zheng     if (ret == -1) {
7297df3aa30SChuan Zheng         error_report("change dirtyrate state failed.");
7307df3aa30SChuan Zheng         return NULL;
7317df3aa30SChuan Zheng     }
7324240dceeSChuan Zheng 
7334240dceeSChuan Zheng     calculate_dirtyrate(config);
7344240dceeSChuan Zheng 
7357df3aa30SChuan Zheng     ret = dirtyrate_set_state(&CalculatingState, DIRTY_RATE_STATUS_MEASURING,
7367df3aa30SChuan Zheng                               DIRTY_RATE_STATUS_MEASURED);
7377df3aa30SChuan Zheng     if (ret == -1) {
7387df3aa30SChuan Zheng         error_report("change dirtyrate state failed.");
7397df3aa30SChuan Zheng     }
74015eb2d64SHyman Huang(黄勇) 
74115eb2d64SHyman Huang(黄勇)     rcu_unregister_thread();
7424240dceeSChuan Zheng     return NULL;
7434240dceeSChuan Zheng }
7444c437254SChuan Zheng 
7450e21bf24SHyman Huang(黄勇) void qmp_calc_dirty_rate(int64_t calc_time,
74634a68001SAndrei Gudkov                          bool has_calc_time_unit,
74734a68001SAndrei Gudkov                          TimeUnit calc_time_unit,
7480e21bf24SHyman Huang(黄勇)                          bool has_sample_pages,
7490e21bf24SHyman Huang(黄勇)                          int64_t sample_pages,
7500e21bf24SHyman Huang(黄勇)                          bool has_mode,
7510e21bf24SHyman Huang(黄勇)                          DirtyRateMeasureMode mode,
7520e21bf24SHyman Huang(黄勇)                          Error **errp)
7534c437254SChuan Zheng {
7544c437254SChuan Zheng     static struct DirtyRateConfig config;
7554c437254SChuan Zheng     QemuThread thread;
7564c437254SChuan Zheng     int ret;
7574c437254SChuan Zheng 
7584c437254SChuan Zheng     /*
7594c437254SChuan Zheng      * If the dirty rate is already being measured, don't attempt to start.
7604c437254SChuan Zheng      */
7614c437254SChuan Zheng     if (qatomic_read(&CalculatingState) == DIRTY_RATE_STATUS_MEASURING) {
7624c437254SChuan Zheng         error_setg(errp, "the dirty rate is already being measured.");
7634c437254SChuan Zheng         return;
7644c437254SChuan Zheng     }
7654c437254SChuan Zheng 
76634a68001SAndrei Gudkov     int64_t calc_time_ms = convert_time_unit(
76734a68001SAndrei Gudkov         calc_time,
76834a68001SAndrei Gudkov         has_calc_time_unit ? calc_time_unit : TIME_UNIT_SECOND,
76934a68001SAndrei Gudkov         TIME_UNIT_MILLISECOND
77034a68001SAndrei Gudkov     );
77134a68001SAndrei Gudkov 
77234a68001SAndrei Gudkov     if (!is_calc_time_valid(calc_time_ms)) {
77334a68001SAndrei Gudkov         error_setg(errp, "Calculation time is out of range [%dms, %dms].",
77434a68001SAndrei Gudkov                          MIN_CALC_TIME_MS, MAX_CALC_TIME_MS);
7754c437254SChuan Zheng         return;
7764c437254SChuan Zheng     }
7774c437254SChuan Zheng 
7780e21bf24SHyman Huang(黄勇)     if (!has_mode) {
7790e21bf24SHyman Huang(黄勇)         mode =  DIRTY_RATE_MEASURE_MODE_PAGE_SAMPLING;
7800e21bf24SHyman Huang(黄勇)     }
7810e21bf24SHyman Huang(黄勇) 
782bd9510d3SZhenzhong Duan     if (has_sample_pages && mode != DIRTY_RATE_MEASURE_MODE_PAGE_SAMPLING) {
783bd9510d3SZhenzhong Duan         error_setg(errp, "sample-pages is used only in page-sampling mode");
7840e21bf24SHyman Huang(黄勇)         return;
7850e21bf24SHyman Huang(黄勇)     }
7860e21bf24SHyman Huang(黄勇) 
7877afa08cdSHyman Huang(黄勇)     if (has_sample_pages) {
7887afa08cdSHyman Huang(黄勇)         if (!is_sample_pages_valid(sample_pages)) {
7897afa08cdSHyman Huang(黄勇)             error_setg(errp, "sample-pages is out of range[%d, %d].",
7907afa08cdSHyman Huang(黄勇)                             MIN_SAMPLE_PAGE_COUNT,
7917afa08cdSHyman Huang(黄勇)                             MAX_SAMPLE_PAGE_COUNT);
7927afa08cdSHyman Huang(黄勇)             return;
7937afa08cdSHyman Huang(黄勇)         }
7947afa08cdSHyman Huang(黄勇)     } else {
7957afa08cdSHyman Huang(黄勇)         sample_pages = DIRTYRATE_DEFAULT_SAMPLE_PAGES;
7967afa08cdSHyman Huang(黄勇)     }
7977afa08cdSHyman Huang(黄勇) 
7984c437254SChuan Zheng     /*
7990e21bf24SHyman Huang(黄勇)      * dirty ring mode only works when kvm dirty ring is enabled.
800826b8bc8SHyman Huang(黄勇)      * on the contrary, dirty bitmap mode is not.
8010e21bf24SHyman Huang(黄勇)      */
802826b8bc8SHyman Huang(黄勇)     if (((mode == DIRTY_RATE_MEASURE_MODE_DIRTY_RING) &&
803826b8bc8SHyman Huang(黄勇)         !kvm_dirty_ring_enabled()) ||
804826b8bc8SHyman Huang(黄勇)         ((mode == DIRTY_RATE_MEASURE_MODE_DIRTY_BITMAP) &&
805826b8bc8SHyman Huang(黄勇)          kvm_dirty_ring_enabled())) {
806826b8bc8SHyman Huang(黄勇)         error_setg(errp, "mode %s is not enabled, use other method instead.",
807826b8bc8SHyman Huang(黄勇)                          DirtyRateMeasureMode_str(mode));
8080e21bf24SHyman Huang(黄勇)          return;
8090e21bf24SHyman Huang(黄勇)     }
8100e21bf24SHyman Huang(黄勇) 
8110e21bf24SHyman Huang(黄勇)     /*
8124c437254SChuan Zheng      * Init calculation state as unstarted.
8134c437254SChuan Zheng      */
8144c437254SChuan Zheng     ret = dirtyrate_set_state(&CalculatingState, CalculatingState,
8154c437254SChuan Zheng                               DIRTY_RATE_STATUS_UNSTARTED);
8164c437254SChuan Zheng     if (ret == -1) {
8174c437254SChuan Zheng         error_setg(errp, "init dirty rate calculation state failed.");
8184c437254SChuan Zheng         return;
8194c437254SChuan Zheng     }
8204c437254SChuan Zheng 
82134a68001SAndrei Gudkov     config.calc_time_ms = calc_time_ms;
8227afa08cdSHyman Huang(黄勇)     config.sample_pages_per_gigabytes = sample_pages;
8230e21bf24SHyman Huang(黄勇)     config.mode = mode;
8240e21bf24SHyman Huang(黄勇) 
8250e21bf24SHyman Huang(黄勇)     cleanup_dirtyrate_stat(config);
8260e21bf24SHyman Huang(黄勇) 
8270e21bf24SHyman Huang(黄勇)     /*
8280e21bf24SHyman Huang(黄勇)      * update dirty rate mode so that we can figure out what mode has
8290e21bf24SHyman Huang(黄勇)      * been used in last calculation
8300e21bf24SHyman Huang(黄勇)      **/
8310e21bf24SHyman Huang(黄勇)     dirtyrate_mode = mode;
8329865d0f6SHyman Huang(黄勇) 
833320a6cccSAndrei Gudkov     init_dirtyrate_stat(config);
8349865d0f6SHyman Huang(黄勇) 
8354c437254SChuan Zheng     qemu_thread_create(&thread, "get_dirtyrate", get_dirtyrate_thread,
8364c437254SChuan Zheng                        (void *)&config, QEMU_THREAD_DETACHED);
8374c437254SChuan Zheng }
8384c437254SChuan Zheng 
83934a68001SAndrei Gudkov 
84034a68001SAndrei Gudkov struct DirtyRateInfo *qmp_query_dirty_rate(bool has_calc_time_unit,
84134a68001SAndrei Gudkov                                            TimeUnit calc_time_unit,
84234a68001SAndrei Gudkov                                            Error **errp)
8434c437254SChuan Zheng {
84434a68001SAndrei Gudkov     return query_dirty_rate_info(
84534a68001SAndrei Gudkov         has_calc_time_unit ? calc_time_unit : TIME_UNIT_SECOND);
8464c437254SChuan Zheng }
847a4a571d9SPeter Xu 
848a4a571d9SPeter Xu void hmp_info_dirty_rate(Monitor *mon, const QDict *qdict)
849a4a571d9SPeter Xu {
85034a68001SAndrei Gudkov     DirtyRateInfo *info = query_dirty_rate_info(TIME_UNIT_SECOND);
851a4a571d9SPeter Xu 
852a4a571d9SPeter Xu     monitor_printf(mon, "Status: %s\n",
853a4a571d9SPeter Xu                    DirtyRateStatus_str(info->status));
854a4a571d9SPeter Xu     monitor_printf(mon, "Start Time: %"PRIi64" (ms)\n",
855a4a571d9SPeter Xu                    info->start_time);
856bd9510d3SZhenzhong Duan     if (info->mode == DIRTY_RATE_MEASURE_MODE_PAGE_SAMPLING) {
857a4a571d9SPeter Xu         monitor_printf(mon, "Sample Pages: %"PRIu64" (per GB)\n",
858a4a571d9SPeter Xu                        info->sample_pages);
859bd9510d3SZhenzhong Duan     }
860a4a571d9SPeter Xu     monitor_printf(mon, "Period: %"PRIi64" (sec)\n",
861a4a571d9SPeter Xu                    info->calc_time);
8620e21bf24SHyman Huang(黄勇)     monitor_printf(mon, "Mode: %s\n",
8630e21bf24SHyman Huang(黄勇)                    DirtyRateMeasureMode_str(info->mode));
864a4a571d9SPeter Xu     monitor_printf(mon, "Dirty rate: ");
865a4a571d9SPeter Xu     if (info->has_dirty_rate) {
866a4a571d9SPeter Xu         monitor_printf(mon, "%"PRIi64" (MB/s)\n", info->dirty_rate);
8670e21bf24SHyman Huang(黄勇)         if (info->has_vcpu_dirty_rate) {
8680e21bf24SHyman Huang(黄勇)             DirtyRateVcpuList *rate, *head = info->vcpu_dirty_rate;
8690e21bf24SHyman Huang(黄勇)             for (rate = head; rate != NULL; rate = rate->next) {
8700e21bf24SHyman Huang(黄勇)                 monitor_printf(mon, "vcpu[%"PRIi64"], Dirty rate: %"PRIi64
8710e21bf24SHyman Huang(黄勇)                                " (MB/s)\n", rate->value->id,
8720e21bf24SHyman Huang(黄勇)                                rate->value->dirty_rate);
8730e21bf24SHyman Huang(黄勇)             }
8740e21bf24SHyman Huang(黄勇)         }
875a4a571d9SPeter Xu     } else {
876a4a571d9SPeter Xu         monitor_printf(mon, "(not ready)\n");
877a4a571d9SPeter Xu     }
8780e21bf24SHyman Huang(黄勇) 
8790e21bf24SHyman Huang(黄勇)     qapi_free_DirtyRateVcpuList(info->vcpu_dirty_rate);
880a4a571d9SPeter Xu     g_free(info);
881a4a571d9SPeter Xu }
882a4a571d9SPeter Xu 
883a4a571d9SPeter Xu void hmp_calc_dirty_rate(Monitor *mon, const QDict *qdict)
884a4a571d9SPeter Xu {
885a4a571d9SPeter Xu     int64_t sec = qdict_get_try_int(qdict, "second", 0);
886a4a571d9SPeter Xu     int64_t sample_pages = qdict_get_try_int(qdict, "sample_pages_per_GB", -1);
887a4a571d9SPeter Xu     bool has_sample_pages = (sample_pages != -1);
8880e21bf24SHyman Huang(黄勇)     bool dirty_ring = qdict_get_try_bool(qdict, "dirty_ring", false);
889826b8bc8SHyman Huang(黄勇)     bool dirty_bitmap = qdict_get_try_bool(qdict, "dirty_bitmap", false);
890826b8bc8SHyman Huang(黄勇)     DirtyRateMeasureMode mode = DIRTY_RATE_MEASURE_MODE_PAGE_SAMPLING;
891a4a571d9SPeter Xu     Error *err = NULL;
892a4a571d9SPeter Xu 
893a4a571d9SPeter Xu     if (!sec) {
894a4a571d9SPeter Xu         monitor_printf(mon, "Incorrect period length specified!\n");
895a4a571d9SPeter Xu         return;
896a4a571d9SPeter Xu     }
897a4a571d9SPeter Xu 
898826b8bc8SHyman Huang(黄勇)     if (dirty_ring && dirty_bitmap) {
899826b8bc8SHyman Huang(黄勇)         monitor_printf(mon, "Either dirty ring or dirty bitmap "
900826b8bc8SHyman Huang(黄勇)                        "can be specified!\n");
901826b8bc8SHyman Huang(黄勇)         return;
902826b8bc8SHyman Huang(黄勇)     }
903826b8bc8SHyman Huang(黄勇) 
904826b8bc8SHyman Huang(黄勇)     if (dirty_bitmap) {
905826b8bc8SHyman Huang(黄勇)         mode = DIRTY_RATE_MEASURE_MODE_DIRTY_BITMAP;
906826b8bc8SHyman Huang(黄勇)     } else if (dirty_ring) {
907826b8bc8SHyman Huang(黄勇)         mode = DIRTY_RATE_MEASURE_MODE_DIRTY_RING;
908826b8bc8SHyman Huang(黄勇)     }
909826b8bc8SHyman Huang(黄勇) 
91034a68001SAndrei Gudkov     qmp_calc_dirty_rate(sec, /* calc-time */
91134a68001SAndrei Gudkov                         false, TIME_UNIT_SECOND, /* calc-time-unit */
91234a68001SAndrei Gudkov                         has_sample_pages, sample_pages,
91334a68001SAndrei Gudkov                         true, mode,
91434a68001SAndrei Gudkov                         &err);
915a4a571d9SPeter Xu     if (err) {
916a4a571d9SPeter Xu         hmp_handle_error(mon, err);
917a4a571d9SPeter Xu         return;
918a4a571d9SPeter Xu     }
919a4a571d9SPeter Xu 
920a4a571d9SPeter Xu     monitor_printf(mon, "Starting dirty rate measurement with period %"PRIi64
921a4a571d9SPeter Xu                    " seconds\n", sec);
922a4a571d9SPeter Xu     monitor_printf(mon, "[Please use 'info dirty_rate' to check results]\n");
923a4a571d9SPeter Xu }
924