xref: /qemu/migration/dirtyrate.c (revision 148b1ad83ca5ea0b0456942d5f40d943a68b6102)
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>
164240dceeSChuan Zheng #include "qapi/error.h"
174240dceeSChuan Zheng #include "cpu.h"
184240dceeSChuan Zheng #include "exec/ramblock.h"
19beeda9b7SJuan Quintela #include "exec/target_page.h"
20826b8bc8SHyman Huang(黄勇) #include "exec/ram_addr.h"
214240dceeSChuan Zheng #include "qemu/rcu_queue.h"
220e21bf24SHyman Huang(黄勇) #include "qemu/main-loop.h"
234240dceeSChuan Zheng #include "qapi/qapi-commands-migration.h"
243ded54b1SChuan Zheng #include "ram.h"
253c0b5dffSChuan Zheng #include "trace.h"
264240dceeSChuan Zheng #include "dirtyrate.h"
27a4a571d9SPeter Xu #include "monitor/hmp.h"
28a4a571d9SPeter Xu #include "monitor/monitor.h"
29a4a571d9SPeter Xu #include "qapi/qmp/qdict.h"
300e21bf24SHyman Huang(黄勇) #include "sysemu/kvm.h"
310e21bf24SHyman Huang(黄勇) #include "sysemu/runstate.h"
320e21bf24SHyman Huang(黄勇) #include "exec/memory.h"
3300a3f9c6SAndrei Gudkov #include "qemu/xxhash.h"
340e21bf24SHyman Huang(黄勇) 
354998a37eSHyman Huang(黄勇) /*
364998a37eSHyman Huang(黄勇)  * total_dirty_pages is procted by BQL and is used
374998a37eSHyman Huang(黄勇)  * to stat dirty pages during the period of two
384998a37eSHyman Huang(黄勇)  * memory_global_dirty_log_sync
394998a37eSHyman Huang(黄勇)  */
404998a37eSHyman Huang(黄勇) uint64_t total_dirty_pages;
414998a37eSHyman Huang(黄勇) 
420e21bf24SHyman Huang(黄勇) typedef struct DirtyPageRecord {
430e21bf24SHyman Huang(黄勇)     uint64_t start_pages;
440e21bf24SHyman Huang(黄勇)     uint64_t end_pages;
450e21bf24SHyman Huang(黄勇) } DirtyPageRecord;
464240dceeSChuan Zheng 
477df3aa30SChuan Zheng static int CalculatingState = DIRTY_RATE_STATUS_UNSTARTED;
48c9a58d71SChuan Zheng static struct DirtyRateStat DirtyStat;
490e21bf24SHyman Huang(黄勇) static DirtyRateMeasureMode dirtyrate_mode =
500e21bf24SHyman Huang(黄勇)                 DIRTY_RATE_MEASURE_MODE_PAGE_SAMPLING;
517df3aa30SChuan Zheng 
528244166dSHyman Huang(黄勇) static int64_t dirty_stat_wait(int64_t msec, int64_t initial_time)
53eca58224SChuan Zheng {
54eca58224SChuan Zheng     int64_t current_time;
55eca58224SChuan Zheng 
56eca58224SChuan Zheng     current_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
57eca58224SChuan Zheng     if ((current_time - initial_time) >= msec) {
58eca58224SChuan Zheng         msec = current_time - initial_time;
59eca58224SChuan Zheng     } else {
60eca58224SChuan Zheng         g_usleep((msec + initial_time - current_time) * 1000);
61eca58224SChuan Zheng     }
62eca58224SChuan Zheng 
63eca58224SChuan Zheng     return msec;
64eca58224SChuan Zheng }
65eca58224SChuan Zheng 
668244166dSHyman Huang(黄勇) static inline void record_dirtypages(DirtyPageRecord *dirty_pages,
678244166dSHyman Huang(黄勇)                                      CPUState *cpu, bool start)
688244166dSHyman Huang(黄勇) {
698244166dSHyman Huang(黄勇)     if (start) {
708244166dSHyman Huang(黄勇)         dirty_pages[cpu->cpu_index].start_pages = cpu->dirty_pages;
718244166dSHyman Huang(黄勇)     } else {
728244166dSHyman Huang(黄勇)         dirty_pages[cpu->cpu_index].end_pages = cpu->dirty_pages;
738244166dSHyman Huang(黄勇)     }
748244166dSHyman Huang(黄勇) }
758244166dSHyman Huang(黄勇) 
768244166dSHyman Huang(黄勇) static int64_t do_calculate_dirtyrate(DirtyPageRecord dirty_pages,
778244166dSHyman Huang(黄勇)                                       int64_t calc_time_ms)
788244166dSHyman Huang(黄勇) {
798244166dSHyman Huang(黄勇)     uint64_t increased_dirty_pages =
808244166dSHyman Huang(黄勇)         dirty_pages.end_pages - dirty_pages.start_pages;
81beeda9b7SJuan Quintela     uint64_t memory_size_MiB = qemu_target_pages_to_MiB(increased_dirty_pages);
828244166dSHyman Huang(黄勇) 
83beeda9b7SJuan Quintela     return memory_size_MiB * 1000 / calc_time_ms;
848244166dSHyman Huang(黄勇) }
858244166dSHyman Huang(黄勇) 
868244166dSHyman Huang(黄勇) void global_dirty_log_change(unsigned int flag, bool start)
878244166dSHyman Huang(黄勇) {
888244166dSHyman Huang(黄勇)     qemu_mutex_lock_iothread();
898244166dSHyman Huang(黄勇)     if (start) {
908244166dSHyman Huang(黄勇)         memory_global_dirty_log_start(flag);
918244166dSHyman Huang(黄勇)     } else {
928244166dSHyman Huang(黄勇)         memory_global_dirty_log_stop(flag);
938244166dSHyman Huang(黄勇)     }
948244166dSHyman Huang(黄勇)     qemu_mutex_unlock_iothread();
958244166dSHyman Huang(黄勇) }
968244166dSHyman Huang(黄勇) 
978244166dSHyman Huang(黄勇) /*
988244166dSHyman Huang(黄勇)  * global_dirty_log_sync
998244166dSHyman Huang(黄勇)  * 1. sync dirty log from kvm
1008244166dSHyman Huang(黄勇)  * 2. stop dirty tracking if needed.
1018244166dSHyman Huang(黄勇)  */
1028244166dSHyman Huang(黄勇) static void global_dirty_log_sync(unsigned int flag, bool one_shot)
1038244166dSHyman Huang(黄勇) {
1048244166dSHyman Huang(黄勇)     qemu_mutex_lock_iothread();
1058244166dSHyman Huang(黄勇)     memory_global_dirty_log_sync();
1068244166dSHyman Huang(黄勇)     if (one_shot) {
1078244166dSHyman Huang(黄勇)         memory_global_dirty_log_stop(flag);
1088244166dSHyman Huang(黄勇)     }
1098244166dSHyman Huang(黄勇)     qemu_mutex_unlock_iothread();
1108244166dSHyman Huang(黄勇) }
1118244166dSHyman Huang(黄勇) 
1128244166dSHyman Huang(黄勇) static DirtyPageRecord *vcpu_dirty_stat_alloc(VcpuStat *stat)
1138244166dSHyman Huang(黄勇) {
1148244166dSHyman Huang(黄勇)     CPUState *cpu;
1158244166dSHyman Huang(黄勇)     int nvcpu = 0;
1168244166dSHyman Huang(黄勇) 
1178244166dSHyman Huang(黄勇)     CPU_FOREACH(cpu) {
1188244166dSHyman Huang(黄勇)         nvcpu++;
1198244166dSHyman Huang(黄勇)     }
1208244166dSHyman Huang(黄勇) 
1218244166dSHyman Huang(黄勇)     stat->nvcpu = nvcpu;
122c5e8d518SMarkus Armbruster     stat->rates = g_new0(DirtyRateVcpu, nvcpu);
1238244166dSHyman Huang(黄勇) 
12466997c42SMarkus Armbruster     return g_new0(DirtyPageRecord, nvcpu);
1258244166dSHyman Huang(黄勇) }
1268244166dSHyman Huang(黄勇) 
1278244166dSHyman Huang(黄勇) static void vcpu_dirty_stat_collect(VcpuStat *stat,
1288244166dSHyman Huang(黄勇)                                     DirtyPageRecord *records,
1298244166dSHyman Huang(黄勇)                                     bool start)
1308244166dSHyman Huang(黄勇) {
1318244166dSHyman Huang(黄勇)     CPUState *cpu;
1328244166dSHyman Huang(黄勇) 
1338244166dSHyman Huang(黄勇)     CPU_FOREACH(cpu) {
1348244166dSHyman Huang(黄勇)         record_dirtypages(records, cpu, start);
1358244166dSHyman Huang(黄勇)     }
1368244166dSHyman Huang(黄勇) }
1378244166dSHyman Huang(黄勇) 
1388244166dSHyman Huang(黄勇) int64_t vcpu_calculate_dirtyrate(int64_t calc_time_ms,
1398244166dSHyman Huang(黄勇)                                  VcpuStat *stat,
1408244166dSHyman Huang(黄勇)                                  unsigned int flag,
1418244166dSHyman Huang(黄勇)                                  bool one_shot)
1428244166dSHyman Huang(黄勇) {
1438244166dSHyman Huang(黄勇)     DirtyPageRecord *records;
1448244166dSHyman Huang(黄勇)     int64_t init_time_ms;
1458244166dSHyman Huang(黄勇)     int64_t duration;
1468244166dSHyman Huang(黄勇)     int64_t dirtyrate;
1478244166dSHyman Huang(黄勇)     int i = 0;
1488244166dSHyman Huang(黄勇)     unsigned int gen_id;
1498244166dSHyman Huang(黄勇) 
1508244166dSHyman Huang(黄勇) retry:
1518244166dSHyman Huang(黄勇)     init_time_ms = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
1528244166dSHyman Huang(黄勇) 
153370ed600SJamie Iles     WITH_QEMU_LOCK_GUARD(&qemu_cpu_list_lock) {
1548244166dSHyman Huang(黄勇)         gen_id = cpu_list_generation_id_get();
1558244166dSHyman Huang(黄勇)         records = vcpu_dirty_stat_alloc(stat);
1568244166dSHyman Huang(黄勇)         vcpu_dirty_stat_collect(stat, records, true);
157370ed600SJamie Iles     }
1588244166dSHyman Huang(黄勇) 
1598244166dSHyman Huang(黄勇)     duration = dirty_stat_wait(calc_time_ms, init_time_ms);
1608244166dSHyman Huang(黄勇) 
1618244166dSHyman Huang(黄勇)     global_dirty_log_sync(flag, one_shot);
1628244166dSHyman Huang(黄勇) 
163370ed600SJamie Iles     WITH_QEMU_LOCK_GUARD(&qemu_cpu_list_lock) {
1648244166dSHyman Huang(黄勇)         if (gen_id != cpu_list_generation_id_get()) {
1658244166dSHyman Huang(黄勇)             g_free(records);
1668244166dSHyman Huang(黄勇)             g_free(stat->rates);
1678244166dSHyman Huang(黄勇)             cpu_list_unlock();
1688244166dSHyman Huang(黄勇)             goto retry;
1698244166dSHyman Huang(黄勇)         }
1708244166dSHyman Huang(黄勇)         vcpu_dirty_stat_collect(stat, records, false);
171370ed600SJamie Iles     }
1728244166dSHyman Huang(黄勇) 
1738244166dSHyman Huang(黄勇)     for (i = 0; i < stat->nvcpu; i++) {
1748244166dSHyman Huang(黄勇)         dirtyrate = do_calculate_dirtyrate(records[i], duration);
1758244166dSHyman Huang(黄勇) 
1768244166dSHyman Huang(黄勇)         stat->rates[i].id = i;
1778244166dSHyman Huang(黄勇)         stat->rates[i].dirty_rate = dirtyrate;
1788244166dSHyman Huang(黄勇) 
1798244166dSHyman Huang(黄勇)         trace_dirtyrate_do_calculate_vcpu(i, dirtyrate);
1808244166dSHyman Huang(黄勇)     }
1818244166dSHyman Huang(黄勇) 
1828244166dSHyman Huang(黄勇)     g_free(records);
1838244166dSHyman Huang(黄勇) 
1848244166dSHyman Huang(黄勇)     return duration;
1858244166dSHyman Huang(黄勇) }
1868244166dSHyman Huang(黄勇) 
187eca58224SChuan Zheng static bool is_sample_period_valid(int64_t sec)
188eca58224SChuan Zheng {
189eca58224SChuan Zheng     if (sec < MIN_FETCH_DIRTYRATE_TIME_SEC ||
190eca58224SChuan Zheng         sec > MAX_FETCH_DIRTYRATE_TIME_SEC) {
191eca58224SChuan Zheng         return false;
192eca58224SChuan Zheng     }
193eca58224SChuan Zheng 
194eca58224SChuan Zheng     return true;
195eca58224SChuan Zheng }
196eca58224SChuan Zheng 
1977afa08cdSHyman Huang(黄勇) static bool is_sample_pages_valid(int64_t pages)
1987afa08cdSHyman Huang(黄勇) {
1997afa08cdSHyman Huang(黄勇)     return pages >= MIN_SAMPLE_PAGE_COUNT &&
2007afa08cdSHyman Huang(黄勇)            pages <= MAX_SAMPLE_PAGE_COUNT;
2017afa08cdSHyman Huang(黄勇) }
2027afa08cdSHyman Huang(黄勇) 
2037df3aa30SChuan Zheng static int dirtyrate_set_state(int *state, int old_state, int new_state)
2047df3aa30SChuan Zheng {
2057df3aa30SChuan Zheng     assert(new_state < DIRTY_RATE_STATUS__MAX);
2063c0b5dffSChuan Zheng     trace_dirtyrate_set_state(DirtyRateStatus_str(new_state));
2077df3aa30SChuan Zheng     if (qatomic_cmpxchg(state, old_state, new_state) == old_state) {
2087df3aa30SChuan Zheng         return 0;
2097df3aa30SChuan Zheng     } else {
2107df3aa30SChuan Zheng         return -1;
2117df3aa30SChuan Zheng     }
2127df3aa30SChuan Zheng }
2137df3aa30SChuan Zheng 
2144c437254SChuan Zheng static struct DirtyRateInfo *query_dirty_rate_info(void)
2154c437254SChuan Zheng {
2160e21bf24SHyman Huang(黄勇)     int i;
2174c437254SChuan Zheng     int64_t dirty_rate = DirtyStat.dirty_rate;
218b21e2380SMarkus Armbruster     struct DirtyRateInfo *info = g_new0(DirtyRateInfo, 1);
2190e21bf24SHyman Huang(黄勇)     DirtyRateVcpuList *head = NULL, **tail = &head;
2204c437254SChuan Zheng 
2214c437254SChuan Zheng     info->status = CalculatingState;
2224c437254SChuan Zheng     info->start_time = DirtyStat.start_time;
2234c437254SChuan Zheng     info->calc_time = DirtyStat.calc_time;
2247afa08cdSHyman Huang(黄勇)     info->sample_pages = DirtyStat.sample_pages;
2250e21bf24SHyman Huang(黄勇)     info->mode = dirtyrate_mode;
2260e21bf24SHyman Huang(黄勇) 
2270e21bf24SHyman Huang(黄勇)     if (qatomic_read(&CalculatingState) == DIRTY_RATE_STATUS_MEASURED) {
2280e21bf24SHyman Huang(黄勇)         info->has_dirty_rate = true;
2290e21bf24SHyman Huang(黄勇)         info->dirty_rate = dirty_rate;
2300e21bf24SHyman Huang(黄勇) 
2310e21bf24SHyman Huang(黄勇)         if (dirtyrate_mode == DIRTY_RATE_MEASURE_MODE_DIRTY_RING) {
2320e21bf24SHyman Huang(黄勇)             /*
2330e21bf24SHyman Huang(黄勇)              * set sample_pages with 0 to indicate page sampling
2340e21bf24SHyman Huang(黄勇)              * isn't enabled
2350e21bf24SHyman Huang(黄勇)              **/
2360e21bf24SHyman Huang(黄勇)             info->sample_pages = 0;
2370e21bf24SHyman Huang(黄勇)             info->has_vcpu_dirty_rate = true;
2380e21bf24SHyman Huang(黄勇)             for (i = 0; i < DirtyStat.dirty_ring.nvcpu; i++) {
239b21e2380SMarkus Armbruster                 DirtyRateVcpu *rate = g_new0(DirtyRateVcpu, 1);
2400e21bf24SHyman Huang(黄勇)                 rate->id = DirtyStat.dirty_ring.rates[i].id;
2410e21bf24SHyman Huang(黄勇)                 rate->dirty_rate = DirtyStat.dirty_ring.rates[i].dirty_rate;
2420e21bf24SHyman Huang(黄勇)                 QAPI_LIST_APPEND(tail, rate);
2430e21bf24SHyman Huang(黄勇)             }
2440e21bf24SHyman Huang(黄勇)             info->vcpu_dirty_rate = head;
2450e21bf24SHyman Huang(黄勇)         }
246826b8bc8SHyman Huang(黄勇) 
247826b8bc8SHyman Huang(黄勇)         if (dirtyrate_mode == DIRTY_RATE_MEASURE_MODE_DIRTY_BITMAP) {
248826b8bc8SHyman Huang(黄勇)             info->sample_pages = 0;
249826b8bc8SHyman Huang(黄勇)         }
2500e21bf24SHyman Huang(黄勇)     }
2514c437254SChuan Zheng 
2523c0b5dffSChuan Zheng     trace_query_dirty_rate_info(DirtyRateStatus_str(CalculatingState));
2533c0b5dffSChuan Zheng 
2544c437254SChuan Zheng     return info;
2554c437254SChuan Zheng }
2564c437254SChuan Zheng 
25771864eadSHyman Huang(黄勇) static void init_dirtyrate_stat(int64_t start_time,
25871864eadSHyman Huang(黄勇)                                 struct DirtyRateConfig config)
259c9a58d71SChuan Zheng {
260c9a58d71SChuan Zheng     DirtyStat.dirty_rate = -1;
261aa84b506SChuan Zheng     DirtyStat.start_time = start_time;
26271864eadSHyman Huang(黄勇)     DirtyStat.calc_time = config.sample_period_seconds;
26371864eadSHyman Huang(黄勇)     DirtyStat.sample_pages = config.sample_pages_per_gigabytes;
26471864eadSHyman Huang(黄勇) 
26571864eadSHyman Huang(黄勇)     switch (config.mode) {
26671864eadSHyman Huang(黄勇)     case DIRTY_RATE_MEASURE_MODE_PAGE_SAMPLING:
26771864eadSHyman Huang(黄勇)         DirtyStat.page_sampling.total_dirty_samples = 0;
26871864eadSHyman Huang(黄勇)         DirtyStat.page_sampling.total_sample_count = 0;
26971864eadSHyman Huang(黄勇)         DirtyStat.page_sampling.total_block_mem_MB = 0;
27071864eadSHyman Huang(黄勇)         break;
27171864eadSHyman Huang(黄勇)     case DIRTY_RATE_MEASURE_MODE_DIRTY_RING:
27271864eadSHyman Huang(黄勇)         DirtyStat.dirty_ring.nvcpu = -1;
27371864eadSHyman Huang(黄勇)         DirtyStat.dirty_ring.rates = NULL;
27471864eadSHyman Huang(黄勇)         break;
27571864eadSHyman Huang(黄勇)     default:
27671864eadSHyman Huang(黄勇)         break;
27771864eadSHyman Huang(黄勇)     }
278c9a58d71SChuan Zheng }
279c9a58d71SChuan Zheng 
2800e21bf24SHyman Huang(黄勇) static void cleanup_dirtyrate_stat(struct DirtyRateConfig config)
2810e21bf24SHyman Huang(黄勇) {
2820e21bf24SHyman Huang(黄勇)     /* last calc-dirty-rate qmp use dirty ring mode */
2830e21bf24SHyman Huang(黄勇)     if (dirtyrate_mode == DIRTY_RATE_MEASURE_MODE_DIRTY_RING) {
2840e21bf24SHyman Huang(黄勇)         free(DirtyStat.dirty_ring.rates);
2850e21bf24SHyman Huang(黄勇)         DirtyStat.dirty_ring.rates = NULL;
2860e21bf24SHyman Huang(黄勇)     }
2870e21bf24SHyman Huang(黄勇) }
2880e21bf24SHyman Huang(黄勇) 
289c9a58d71SChuan Zheng static void update_dirtyrate_stat(struct RamblockDirtyInfo *info)
290c9a58d71SChuan Zheng {
29171864eadSHyman Huang(黄勇)     DirtyStat.page_sampling.total_dirty_samples += info->sample_dirty_count;
29271864eadSHyman Huang(黄勇)     DirtyStat.page_sampling.total_sample_count += info->sample_pages_count;
293c9a58d71SChuan Zheng     /* size of total pages in MB */
294beeda9b7SJuan Quintela     DirtyStat.page_sampling.total_block_mem_MB +=
295beeda9b7SJuan Quintela         qemu_target_pages_to_MiB(info->ramblock_pages);
296c9a58d71SChuan Zheng }
297c9a58d71SChuan Zheng 
298c9a58d71SChuan Zheng static void update_dirtyrate(uint64_t msec)
299c9a58d71SChuan Zheng {
300c9a58d71SChuan Zheng     uint64_t dirtyrate;
30171864eadSHyman Huang(黄勇)     uint64_t total_dirty_samples = DirtyStat.page_sampling.total_dirty_samples;
30271864eadSHyman Huang(黄勇)     uint64_t total_sample_count = DirtyStat.page_sampling.total_sample_count;
30371864eadSHyman Huang(黄勇)     uint64_t total_block_mem_MB = DirtyStat.page_sampling.total_block_mem_MB;
304c9a58d71SChuan Zheng 
305c9a58d71SChuan Zheng     dirtyrate = total_dirty_samples * total_block_mem_MB *
306c9a58d71SChuan Zheng                 1000 / (total_sample_count * msec);
307c9a58d71SChuan Zheng 
308c9a58d71SChuan Zheng     DirtyStat.dirty_rate = dirtyrate;
309c9a58d71SChuan Zheng }
3107df3aa30SChuan Zheng 
311ba0e519fSChuan Zheng /*
31200a3f9c6SAndrei Gudkov  * Compute hash of a single page of size TARGET_PAGE_SIZE.
31300a3f9c6SAndrei Gudkov  */
31400a3f9c6SAndrei Gudkov static uint32_t compute_page_hash(void *ptr)
31500a3f9c6SAndrei Gudkov {
316edd83a70SJuan Quintela     size_t page_size = qemu_target_page_size();
31700a3f9c6SAndrei Gudkov     uint32_t i;
31800a3f9c6SAndrei Gudkov     uint64_t v1, v2, v3, v4;
31900a3f9c6SAndrei Gudkov     uint64_t res;
32000a3f9c6SAndrei Gudkov     const uint64_t *p = ptr;
32100a3f9c6SAndrei Gudkov 
32200a3f9c6SAndrei Gudkov     v1 = QEMU_XXHASH_SEED + XXH_PRIME64_1 + XXH_PRIME64_2;
32300a3f9c6SAndrei Gudkov     v2 = QEMU_XXHASH_SEED + XXH_PRIME64_2;
32400a3f9c6SAndrei Gudkov     v3 = QEMU_XXHASH_SEED + 0;
32500a3f9c6SAndrei Gudkov     v4 = QEMU_XXHASH_SEED - XXH_PRIME64_1;
326edd83a70SJuan Quintela     for (i = 0; i < page_size / 8; i += 4) {
32700a3f9c6SAndrei Gudkov         v1 = XXH64_round(v1, p[i + 0]);
32800a3f9c6SAndrei Gudkov         v2 = XXH64_round(v2, p[i + 1]);
32900a3f9c6SAndrei Gudkov         v3 = XXH64_round(v3, p[i + 2]);
33000a3f9c6SAndrei Gudkov         v4 = XXH64_round(v4, p[i + 3]);
33100a3f9c6SAndrei Gudkov     }
33200a3f9c6SAndrei Gudkov     res = XXH64_mergerounds(v1, v2, v3, v4);
333edd83a70SJuan Quintela     res += page_size;
33400a3f9c6SAndrei Gudkov     res = XXH64_avalanche(res);
33500a3f9c6SAndrei Gudkov     return (uint32_t)(res & UINT32_MAX);
33600a3f9c6SAndrei Gudkov }
33700a3f9c6SAndrei Gudkov 
33800a3f9c6SAndrei Gudkov 
33900a3f9c6SAndrei Gudkov /*
340ba0e519fSChuan Zheng  * get hash result for the sampled memory with length of TARGET_PAGE_SIZE
341ba0e519fSChuan Zheng  * in ramblock, which starts from ramblock base address.
342ba0e519fSChuan Zheng  */
343ba0e519fSChuan Zheng static uint32_t get_ramblock_vfn_hash(struct RamblockDirtyInfo *info,
344ba0e519fSChuan Zheng                                       uint64_t vfn)
345ba0e519fSChuan Zheng {
34600a3f9c6SAndrei Gudkov     uint32_t hash;
347ba0e519fSChuan Zheng 
348edd83a70SJuan Quintela     hash = compute_page_hash(info->ramblock_addr +
349edd83a70SJuan Quintela                              vfn * qemu_target_page_size());
350ba0e519fSChuan Zheng 
35100a3f9c6SAndrei Gudkov     trace_get_ramblock_vfn_hash(info->idstr, vfn, hash);
35200a3f9c6SAndrei Gudkov     return hash;
353ba0e519fSChuan Zheng }
354ba0e519fSChuan Zheng 
355ba0e519fSChuan Zheng static bool save_ramblock_hash(struct RamblockDirtyInfo *info)
356ba0e519fSChuan Zheng {
357ba0e519fSChuan Zheng     unsigned int sample_pages_count;
358ba0e519fSChuan Zheng     int i;
359ba0e519fSChuan Zheng     GRand *rand;
360ba0e519fSChuan Zheng 
361ba0e519fSChuan Zheng     sample_pages_count = info->sample_pages_count;
362ba0e519fSChuan Zheng 
363ba0e519fSChuan Zheng     /* ramblock size less than one page, return success to skip this ramblock */
364ba0e519fSChuan Zheng     if (unlikely(info->ramblock_pages == 0 || sample_pages_count == 0)) {
365ba0e519fSChuan Zheng         return true;
366ba0e519fSChuan Zheng     }
367ba0e519fSChuan Zheng 
368ba0e519fSChuan Zheng     info->hash_result = g_try_malloc0_n(sample_pages_count,
369ba0e519fSChuan Zheng                                         sizeof(uint32_t));
370ba0e519fSChuan Zheng     if (!info->hash_result) {
371ba0e519fSChuan Zheng         return false;
372ba0e519fSChuan Zheng     }
373ba0e519fSChuan Zheng 
374ba0e519fSChuan Zheng     info->sample_page_vfn = g_try_malloc0_n(sample_pages_count,
375ba0e519fSChuan Zheng                                             sizeof(uint64_t));
376ba0e519fSChuan Zheng     if (!info->sample_page_vfn) {
377ba0e519fSChuan Zheng         g_free(info->hash_result);
378ba0e519fSChuan Zheng         return false;
379ba0e519fSChuan Zheng     }
380ba0e519fSChuan Zheng 
381ba0e519fSChuan Zheng     rand  = g_rand_new();
382ba0e519fSChuan Zheng     for (i = 0; i < sample_pages_count; i++) {
383ba0e519fSChuan Zheng         info->sample_page_vfn[i] = g_rand_int_range(rand, 0,
384ba0e519fSChuan Zheng                                                     info->ramblock_pages - 1);
385ba0e519fSChuan Zheng         info->hash_result[i] = get_ramblock_vfn_hash(info,
386ba0e519fSChuan Zheng                                                      info->sample_page_vfn[i]);
387ba0e519fSChuan Zheng     }
388ba0e519fSChuan Zheng     g_rand_free(rand);
389ba0e519fSChuan Zheng 
390ba0e519fSChuan Zheng     return true;
391ba0e519fSChuan Zheng }
392ba0e519fSChuan Zheng 
393ba0e519fSChuan Zheng static void get_ramblock_dirty_info(RAMBlock *block,
394ba0e519fSChuan Zheng                                     struct RamblockDirtyInfo *info,
395ba0e519fSChuan Zheng                                     struct DirtyRateConfig *config)
396ba0e519fSChuan Zheng {
397ba0e519fSChuan Zheng     uint64_t sample_pages_per_gigabytes = config->sample_pages_per_gigabytes;
398ba0e519fSChuan Zheng 
399ba0e519fSChuan Zheng     /* Right shift 30 bits to calc ramblock size in GB */
400ba0e519fSChuan Zheng     info->sample_pages_count = (qemu_ram_get_used_length(block) *
401ba0e519fSChuan Zheng                                 sample_pages_per_gigabytes) >> 30;
402ba0e519fSChuan Zheng     /* Right shift TARGET_PAGE_BITS to calc page count */
403ba0e519fSChuan Zheng     info->ramblock_pages = qemu_ram_get_used_length(block) >>
404*148b1ad8SJuan Quintela                            qemu_target_page_bits();
405ba0e519fSChuan Zheng     info->ramblock_addr = qemu_ram_get_host_addr(block);
406ba0e519fSChuan Zheng     strcpy(info->idstr, qemu_ram_get_idstr(block));
407ba0e519fSChuan Zheng }
408ba0e519fSChuan Zheng 
409cf0bbb49SChuan Zheng static void free_ramblock_dirty_info(struct RamblockDirtyInfo *infos, int count)
410cf0bbb49SChuan Zheng {
411cf0bbb49SChuan Zheng     int i;
412cf0bbb49SChuan Zheng 
413cf0bbb49SChuan Zheng     if (!infos) {
414cf0bbb49SChuan Zheng         return;
415cf0bbb49SChuan Zheng     }
416cf0bbb49SChuan Zheng 
417cf0bbb49SChuan Zheng     for (i = 0; i < count; i++) {
418cf0bbb49SChuan Zheng         g_free(infos[i].sample_page_vfn);
419cf0bbb49SChuan Zheng         g_free(infos[i].hash_result);
420cf0bbb49SChuan Zheng     }
421cf0bbb49SChuan Zheng     g_free(infos);
422cf0bbb49SChuan Zheng }
423cf0bbb49SChuan Zheng 
424f82583cdSChuan Zheng static bool skip_sample_ramblock(RAMBlock *block)
425f82583cdSChuan Zheng {
426f82583cdSChuan Zheng     /*
427f82583cdSChuan Zheng      * Sample only blocks larger than MIN_RAMBLOCK_SIZE.
428f82583cdSChuan Zheng      */
429f82583cdSChuan Zheng     if (qemu_ram_get_used_length(block) < (MIN_RAMBLOCK_SIZE << 10)) {
4303c0b5dffSChuan Zheng         trace_skip_sample_ramblock(block->idstr,
4313c0b5dffSChuan Zheng                                    qemu_ram_get_used_length(block));
432f82583cdSChuan Zheng         return true;
433f82583cdSChuan Zheng     }
434f82583cdSChuan Zheng 
435f82583cdSChuan Zheng     return false;
436f82583cdSChuan Zheng }
437f82583cdSChuan Zheng 
438ba0e519fSChuan Zheng static bool record_ramblock_hash_info(struct RamblockDirtyInfo **block_dinfo,
439ba0e519fSChuan Zheng                                       struct DirtyRateConfig config,
440ba0e519fSChuan Zheng                                       int *block_count)
441ba0e519fSChuan Zheng {
442ba0e519fSChuan Zheng     struct RamblockDirtyInfo *info = NULL;
443ba0e519fSChuan Zheng     struct RamblockDirtyInfo *dinfo = NULL;
444ba0e519fSChuan Zheng     RAMBlock *block = NULL;
445ba0e519fSChuan Zheng     int total_count = 0;
446ba0e519fSChuan Zheng     int index = 0;
447ba0e519fSChuan Zheng     bool ret = false;
448ba0e519fSChuan Zheng 
449ba0e519fSChuan Zheng     RAMBLOCK_FOREACH_MIGRATABLE(block) {
450f82583cdSChuan Zheng         if (skip_sample_ramblock(block)) {
451f82583cdSChuan Zheng             continue;
452f82583cdSChuan Zheng         }
453ba0e519fSChuan Zheng         total_count++;
454ba0e519fSChuan Zheng     }
455ba0e519fSChuan Zheng 
456ba0e519fSChuan Zheng     dinfo = g_try_malloc0_n(total_count, sizeof(struct RamblockDirtyInfo));
457ba0e519fSChuan Zheng     if (dinfo == NULL) {
458ba0e519fSChuan Zheng         goto out;
459ba0e519fSChuan Zheng     }
460ba0e519fSChuan Zheng 
461ba0e519fSChuan Zheng     RAMBLOCK_FOREACH_MIGRATABLE(block) {
462f82583cdSChuan Zheng         if (skip_sample_ramblock(block)) {
463f82583cdSChuan Zheng             continue;
464f82583cdSChuan Zheng         }
465ba0e519fSChuan Zheng         if (index >= total_count) {
466ba0e519fSChuan Zheng             break;
467ba0e519fSChuan Zheng         }
468ba0e519fSChuan Zheng         info = &dinfo[index];
469ba0e519fSChuan Zheng         get_ramblock_dirty_info(block, info, &config);
470ba0e519fSChuan Zheng         if (!save_ramblock_hash(info)) {
471ba0e519fSChuan Zheng             goto out;
472ba0e519fSChuan Zheng         }
473ba0e519fSChuan Zheng         index++;
474ba0e519fSChuan Zheng     }
475ba0e519fSChuan Zheng     ret = true;
476ba0e519fSChuan Zheng 
477ba0e519fSChuan Zheng out:
478ba0e519fSChuan Zheng     *block_count = index;
479ba0e519fSChuan Zheng     *block_dinfo = dinfo;
480ba0e519fSChuan Zheng     return ret;
481ba0e519fSChuan Zheng }
482ba0e519fSChuan Zheng 
4839c04387bSChuan Zheng static void calc_page_dirty_rate(struct RamblockDirtyInfo *info)
4849c04387bSChuan Zheng {
48500a3f9c6SAndrei Gudkov     uint32_t hash;
4869c04387bSChuan Zheng     int i;
4879c04387bSChuan Zheng 
4889c04387bSChuan Zheng     for (i = 0; i < info->sample_pages_count; i++) {
48900a3f9c6SAndrei Gudkov         hash = get_ramblock_vfn_hash(info, info->sample_page_vfn[i]);
49000a3f9c6SAndrei Gudkov         if (hash != info->hash_result[i]) {
49100a3f9c6SAndrei Gudkov             trace_calc_page_dirty_rate(info->idstr, hash, info->hash_result[i]);
4929c04387bSChuan Zheng             info->sample_dirty_count++;
4939c04387bSChuan Zheng         }
4949c04387bSChuan Zheng     }
4959c04387bSChuan Zheng }
4969c04387bSChuan Zheng 
4979c04387bSChuan Zheng static struct RamblockDirtyInfo *
4989c04387bSChuan Zheng find_block_matched(RAMBlock *block, int count,
4999c04387bSChuan Zheng                   struct RamblockDirtyInfo *infos)
5009c04387bSChuan Zheng {
5019c04387bSChuan Zheng     int i;
5029c04387bSChuan Zheng 
5039c04387bSChuan Zheng     for (i = 0; i < count; i++) {
5049c04387bSChuan Zheng         if (!strcmp(infos[i].idstr, qemu_ram_get_idstr(block))) {
5059c04387bSChuan Zheng             break;
5069c04387bSChuan Zheng         }
5079c04387bSChuan Zheng     }
5089c04387bSChuan Zheng 
5099c04387bSChuan Zheng     if (i == count) {
5109c04387bSChuan Zheng         return NULL;
5119c04387bSChuan Zheng     }
5129c04387bSChuan Zheng 
5139c04387bSChuan Zheng     if (infos[i].ramblock_addr != qemu_ram_get_host_addr(block) ||
5149c04387bSChuan Zheng         infos[i].ramblock_pages !=
515*148b1ad8SJuan Quintela             (qemu_ram_get_used_length(block) >> qemu_target_page_bits())) {
5163c0b5dffSChuan Zheng         trace_find_page_matched(block->idstr);
5179c04387bSChuan Zheng         return NULL;
5189c04387bSChuan Zheng     }
5199c04387bSChuan Zheng 
52066997c42SMarkus Armbruster     return &infos[i];
5219c04387bSChuan Zheng }
5229c04387bSChuan Zheng 
5239c04387bSChuan Zheng static bool compare_page_hash_info(struct RamblockDirtyInfo *info,
5249c04387bSChuan Zheng                                   int block_count)
5259c04387bSChuan Zheng {
5269c04387bSChuan Zheng     struct RamblockDirtyInfo *block_dinfo = NULL;
5279c04387bSChuan Zheng     RAMBlock *block = NULL;
5289c04387bSChuan Zheng 
5299c04387bSChuan Zheng     RAMBLOCK_FOREACH_MIGRATABLE(block) {
530f82583cdSChuan Zheng         if (skip_sample_ramblock(block)) {
531f82583cdSChuan Zheng             continue;
532f82583cdSChuan Zheng         }
5339c04387bSChuan Zheng         block_dinfo = find_block_matched(block, block_count, info);
5349c04387bSChuan Zheng         if (block_dinfo == NULL) {
5359c04387bSChuan Zheng             continue;
5369c04387bSChuan Zheng         }
5379c04387bSChuan Zheng         calc_page_dirty_rate(block_dinfo);
5389c04387bSChuan Zheng         update_dirtyrate_stat(block_dinfo);
5399c04387bSChuan Zheng     }
5409c04387bSChuan Zheng 
54171864eadSHyman Huang(黄勇)     if (DirtyStat.page_sampling.total_sample_count == 0) {
5429c04387bSChuan Zheng         return false;
5439c04387bSChuan Zheng     }
5449c04387bSChuan Zheng 
5459c04387bSChuan Zheng     return true;
5469c04387bSChuan Zheng }
5479c04387bSChuan Zheng 
548826b8bc8SHyman Huang(黄勇) static inline void record_dirtypages_bitmap(DirtyPageRecord *dirty_pages,
549826b8bc8SHyman Huang(黄勇)                                             bool start)
550826b8bc8SHyman Huang(黄勇) {
551826b8bc8SHyman Huang(黄勇)     if (start) {
552826b8bc8SHyman Huang(黄勇)         dirty_pages->start_pages = total_dirty_pages;
553826b8bc8SHyman Huang(黄勇)     } else {
554826b8bc8SHyman Huang(黄勇)         dirty_pages->end_pages = total_dirty_pages;
555826b8bc8SHyman Huang(黄勇)     }
556826b8bc8SHyman Huang(黄勇) }
557826b8bc8SHyman Huang(黄勇) 
558826b8bc8SHyman Huang(黄勇) static inline void dirtyrate_manual_reset_protect(void)
559826b8bc8SHyman Huang(黄勇) {
560826b8bc8SHyman Huang(黄勇)     RAMBlock *block = NULL;
561826b8bc8SHyman Huang(黄勇) 
562826b8bc8SHyman Huang(黄勇)     WITH_RCU_READ_LOCK_GUARD() {
563826b8bc8SHyman Huang(黄勇)         RAMBLOCK_FOREACH_MIGRATABLE(block) {
564826b8bc8SHyman Huang(黄勇)             memory_region_clear_dirty_bitmap(block->mr, 0,
565826b8bc8SHyman Huang(黄勇)                                              block->used_length);
566826b8bc8SHyman Huang(黄勇)         }
567826b8bc8SHyman Huang(黄勇)     }
568826b8bc8SHyman Huang(黄勇) }
569826b8bc8SHyman Huang(黄勇) 
570826b8bc8SHyman Huang(黄勇) static void calculate_dirtyrate_dirty_bitmap(struct DirtyRateConfig config)
571826b8bc8SHyman Huang(黄勇) {
572826b8bc8SHyman Huang(黄勇)     int64_t msec = 0;
573826b8bc8SHyman Huang(黄勇)     int64_t start_time;
574826b8bc8SHyman Huang(黄勇)     DirtyPageRecord dirty_pages;
575826b8bc8SHyman Huang(黄勇) 
576826b8bc8SHyman Huang(黄勇)     qemu_mutex_lock_iothread();
577826b8bc8SHyman Huang(黄勇)     memory_global_dirty_log_start(GLOBAL_DIRTY_DIRTY_RATE);
578826b8bc8SHyman Huang(黄勇) 
579826b8bc8SHyman Huang(黄勇)     /*
580826b8bc8SHyman Huang(黄勇)      * 1'round of log sync may return all 1 bits with
581826b8bc8SHyman Huang(黄勇)      * KVM_DIRTY_LOG_INITIALLY_SET enable
582826b8bc8SHyman Huang(黄勇)      * skip it unconditionally and start dirty tracking
583826b8bc8SHyman Huang(黄勇)      * from 2'round of log sync
584826b8bc8SHyman Huang(黄勇)      */
585826b8bc8SHyman Huang(黄勇)     memory_global_dirty_log_sync();
586826b8bc8SHyman Huang(黄勇) 
587826b8bc8SHyman Huang(黄勇)     /*
588826b8bc8SHyman Huang(黄勇)      * reset page protect manually and unconditionally.
589826b8bc8SHyman Huang(黄勇)      * this make sure kvm dirty log be cleared if
590826b8bc8SHyman Huang(黄勇)      * KVM_DIRTY_LOG_MANUAL_PROTECT_ENABLE cap is enabled.
591826b8bc8SHyman Huang(黄勇)      */
592826b8bc8SHyman Huang(黄勇)     dirtyrate_manual_reset_protect();
593826b8bc8SHyman Huang(黄勇)     qemu_mutex_unlock_iothread();
594826b8bc8SHyman Huang(黄勇) 
595826b8bc8SHyman Huang(黄勇)     record_dirtypages_bitmap(&dirty_pages, true);
596826b8bc8SHyman Huang(黄勇) 
597826b8bc8SHyman Huang(黄勇)     start_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
598826b8bc8SHyman Huang(黄勇)     DirtyStat.start_time = start_time / 1000;
599826b8bc8SHyman Huang(黄勇) 
600826b8bc8SHyman Huang(黄勇)     msec = config.sample_period_seconds * 1000;
6018244166dSHyman Huang(黄勇)     msec = dirty_stat_wait(msec, start_time);
602826b8bc8SHyman Huang(黄勇)     DirtyStat.calc_time = msec / 1000;
603826b8bc8SHyman Huang(黄勇) 
604826b8bc8SHyman Huang(黄勇)     /*
6058244166dSHyman Huang(黄勇)      * do two things.
606826b8bc8SHyman Huang(黄勇)      * 1. fetch dirty bitmap from kvm
607826b8bc8SHyman Huang(黄勇)      * 2. stop dirty tracking
608826b8bc8SHyman Huang(黄勇)      */
6098244166dSHyman Huang(黄勇)     global_dirty_log_sync(GLOBAL_DIRTY_DIRTY_RATE, true);
610826b8bc8SHyman Huang(黄勇) 
611826b8bc8SHyman Huang(黄勇)     record_dirtypages_bitmap(&dirty_pages, false);
612826b8bc8SHyman Huang(黄勇) 
6138244166dSHyman Huang(黄勇)     DirtyStat.dirty_rate = do_calculate_dirtyrate(dirty_pages, msec);
614826b8bc8SHyman Huang(黄勇) }
615826b8bc8SHyman Huang(黄勇) 
6160e21bf24SHyman Huang(黄勇) static void calculate_dirtyrate_dirty_ring(struct DirtyRateConfig config)
6170e21bf24SHyman Huang(黄勇) {
6188244166dSHyman Huang(黄勇)     int64_t duration;
6190e21bf24SHyman Huang(黄勇)     uint64_t dirtyrate = 0;
6200e21bf24SHyman Huang(黄勇)     uint64_t dirtyrate_sum = 0;
6210e21bf24SHyman Huang(黄勇)     int i = 0;
6220e21bf24SHyman Huang(黄勇) 
6238244166dSHyman Huang(黄勇)     /* start log sync */
6248244166dSHyman Huang(黄勇)     global_dirty_log_change(GLOBAL_DIRTY_DIRTY_RATE, true);
6250e21bf24SHyman Huang(黄勇) 
6268244166dSHyman Huang(黄勇)     DirtyStat.start_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME) / 1000;
6270e21bf24SHyman Huang(黄勇) 
6288244166dSHyman Huang(黄勇)     /* calculate vcpu dirtyrate */
6298244166dSHyman Huang(黄勇)     duration = vcpu_calculate_dirtyrate(config.sample_period_seconds * 1000,
6308244166dSHyman Huang(黄勇)                                         &DirtyStat.dirty_ring,
6318244166dSHyman Huang(黄勇)                                         GLOBAL_DIRTY_DIRTY_RATE,
6328244166dSHyman Huang(黄勇)                                         true);
6330e21bf24SHyman Huang(黄勇) 
6348244166dSHyman Huang(黄勇)     DirtyStat.calc_time = duration / 1000;
6350e21bf24SHyman Huang(黄勇) 
6368244166dSHyman Huang(黄勇)     /* calculate vm dirtyrate */
6370e21bf24SHyman Huang(黄勇)     for (i = 0; i < DirtyStat.dirty_ring.nvcpu; i++) {
6388244166dSHyman Huang(黄勇)         dirtyrate = DirtyStat.dirty_ring.rates[i].dirty_rate;
6390e21bf24SHyman Huang(黄勇)         DirtyStat.dirty_ring.rates[i].dirty_rate = dirtyrate;
6400e21bf24SHyman Huang(黄勇)         dirtyrate_sum += dirtyrate;
6410e21bf24SHyman Huang(黄勇)     }
6420e21bf24SHyman Huang(黄勇) 
6430e21bf24SHyman Huang(黄勇)     DirtyStat.dirty_rate = dirtyrate_sum;
6440e21bf24SHyman Huang(黄勇) }
6450e21bf24SHyman Huang(黄勇) 
6460e21bf24SHyman Huang(黄勇) static void calculate_dirtyrate_sample_vm(struct DirtyRateConfig config)
6474240dceeSChuan Zheng {
648cf0bbb49SChuan Zheng     struct RamblockDirtyInfo *block_dinfo = NULL;
649cf0bbb49SChuan Zheng     int block_count = 0;
650cf0bbb49SChuan Zheng     int64_t msec = 0;
651cf0bbb49SChuan Zheng     int64_t initial_time;
652cf0bbb49SChuan Zheng 
653cf0bbb49SChuan Zheng     rcu_read_lock();
654cf0bbb49SChuan Zheng     initial_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
655cf0bbb49SChuan Zheng     if (!record_ramblock_hash_info(&block_dinfo, config, &block_count)) {
656cf0bbb49SChuan Zheng         goto out;
657cf0bbb49SChuan Zheng     }
658cf0bbb49SChuan Zheng     rcu_read_unlock();
659cf0bbb49SChuan Zheng 
660cf0bbb49SChuan Zheng     msec = config.sample_period_seconds * 1000;
6618244166dSHyman Huang(黄勇)     msec = dirty_stat_wait(msec, initial_time);
6624c437254SChuan Zheng     DirtyStat.start_time = initial_time / 1000;
6634c437254SChuan Zheng     DirtyStat.calc_time = msec / 1000;
664cf0bbb49SChuan Zheng 
665cf0bbb49SChuan Zheng     rcu_read_lock();
666cf0bbb49SChuan Zheng     if (!compare_page_hash_info(block_dinfo, block_count)) {
667cf0bbb49SChuan Zheng         goto out;
668cf0bbb49SChuan Zheng     }
669cf0bbb49SChuan Zheng 
670cf0bbb49SChuan Zheng     update_dirtyrate(msec);
671cf0bbb49SChuan Zheng 
672cf0bbb49SChuan Zheng out:
673cf0bbb49SChuan Zheng     rcu_read_unlock();
674cf0bbb49SChuan Zheng     free_ramblock_dirty_info(block_dinfo, block_count);
6754240dceeSChuan Zheng }
6764240dceeSChuan Zheng 
6770e21bf24SHyman Huang(黄勇) static void calculate_dirtyrate(struct DirtyRateConfig config)
6780e21bf24SHyman Huang(黄勇) {
679826b8bc8SHyman Huang(黄勇)     if (config.mode == DIRTY_RATE_MEASURE_MODE_DIRTY_BITMAP) {
680826b8bc8SHyman Huang(黄勇)         calculate_dirtyrate_dirty_bitmap(config);
681826b8bc8SHyman Huang(黄勇)     } else if (config.mode == DIRTY_RATE_MEASURE_MODE_DIRTY_RING) {
6820e21bf24SHyman Huang(黄勇)         calculate_dirtyrate_dirty_ring(config);
6830e21bf24SHyman Huang(黄勇)     } else {
6840e21bf24SHyman Huang(黄勇)         calculate_dirtyrate_sample_vm(config);
6850e21bf24SHyman Huang(黄勇)     }
6860e21bf24SHyman Huang(黄勇) 
6870e21bf24SHyman Huang(黄勇)     trace_dirtyrate_calculate(DirtyStat.dirty_rate);
6880e21bf24SHyman Huang(黄勇) }
6890e21bf24SHyman Huang(黄勇) 
6904240dceeSChuan Zheng void *get_dirtyrate_thread(void *arg)
6914240dceeSChuan Zheng {
6924240dceeSChuan Zheng     struct DirtyRateConfig config = *(struct DirtyRateConfig *)arg;
6937df3aa30SChuan Zheng     int ret;
69415eb2d64SHyman Huang(黄勇)     rcu_register_thread();
6957df3aa30SChuan Zheng 
6967df3aa30SChuan Zheng     ret = dirtyrate_set_state(&CalculatingState, DIRTY_RATE_STATUS_UNSTARTED,
6977df3aa30SChuan Zheng                               DIRTY_RATE_STATUS_MEASURING);
6987df3aa30SChuan Zheng     if (ret == -1) {
6997df3aa30SChuan Zheng         error_report("change dirtyrate state failed.");
7007df3aa30SChuan Zheng         return NULL;
7017df3aa30SChuan Zheng     }
7024240dceeSChuan Zheng 
7034240dceeSChuan Zheng     calculate_dirtyrate(config);
7044240dceeSChuan Zheng 
7057df3aa30SChuan Zheng     ret = dirtyrate_set_state(&CalculatingState, DIRTY_RATE_STATUS_MEASURING,
7067df3aa30SChuan Zheng                               DIRTY_RATE_STATUS_MEASURED);
7077df3aa30SChuan Zheng     if (ret == -1) {
7087df3aa30SChuan Zheng         error_report("change dirtyrate state failed.");
7097df3aa30SChuan Zheng     }
71015eb2d64SHyman Huang(黄勇) 
71115eb2d64SHyman Huang(黄勇)     rcu_unregister_thread();
7124240dceeSChuan Zheng     return NULL;
7134240dceeSChuan Zheng }
7144c437254SChuan Zheng 
7150e21bf24SHyman Huang(黄勇) void qmp_calc_dirty_rate(int64_t calc_time,
7160e21bf24SHyman Huang(黄勇)                          bool has_sample_pages,
7170e21bf24SHyman Huang(黄勇)                          int64_t sample_pages,
7180e21bf24SHyman Huang(黄勇)                          bool has_mode,
7190e21bf24SHyman Huang(黄勇)                          DirtyRateMeasureMode mode,
7200e21bf24SHyman Huang(黄勇)                          Error **errp)
7214c437254SChuan Zheng {
7224c437254SChuan Zheng     static struct DirtyRateConfig config;
7234c437254SChuan Zheng     QemuThread thread;
7244c437254SChuan Zheng     int ret;
7259865d0f6SHyman Huang(黄勇)     int64_t start_time;
7264c437254SChuan Zheng 
7274c437254SChuan Zheng     /*
7284c437254SChuan Zheng      * If the dirty rate is already being measured, don't attempt to start.
7294c437254SChuan Zheng      */
7304c437254SChuan Zheng     if (qatomic_read(&CalculatingState) == DIRTY_RATE_STATUS_MEASURING) {
7314c437254SChuan Zheng         error_setg(errp, "the dirty rate is already being measured.");
7324c437254SChuan Zheng         return;
7334c437254SChuan Zheng     }
7344c437254SChuan Zheng 
7354c437254SChuan Zheng     if (!is_sample_period_valid(calc_time)) {
7364c437254SChuan Zheng         error_setg(errp, "calc-time is out of range[%d, %d].",
7374c437254SChuan Zheng                          MIN_FETCH_DIRTYRATE_TIME_SEC,
7384c437254SChuan Zheng                          MAX_FETCH_DIRTYRATE_TIME_SEC);
7394c437254SChuan Zheng         return;
7404c437254SChuan Zheng     }
7414c437254SChuan Zheng 
7420e21bf24SHyman Huang(黄勇)     if (!has_mode) {
7430e21bf24SHyman Huang(黄勇)         mode =  DIRTY_RATE_MEASURE_MODE_PAGE_SAMPLING;
7440e21bf24SHyman Huang(黄勇)     }
7450e21bf24SHyman Huang(黄勇) 
746bd9510d3SZhenzhong Duan     if (has_sample_pages && mode != DIRTY_RATE_MEASURE_MODE_PAGE_SAMPLING) {
747bd9510d3SZhenzhong Duan         error_setg(errp, "sample-pages is used only in page-sampling mode");
7480e21bf24SHyman Huang(黄勇)         return;
7490e21bf24SHyman Huang(黄勇)     }
7500e21bf24SHyman Huang(黄勇) 
7517afa08cdSHyman Huang(黄勇)     if (has_sample_pages) {
7527afa08cdSHyman Huang(黄勇)         if (!is_sample_pages_valid(sample_pages)) {
7537afa08cdSHyman Huang(黄勇)             error_setg(errp, "sample-pages is out of range[%d, %d].",
7547afa08cdSHyman Huang(黄勇)                             MIN_SAMPLE_PAGE_COUNT,
7557afa08cdSHyman Huang(黄勇)                             MAX_SAMPLE_PAGE_COUNT);
7567afa08cdSHyman Huang(黄勇)             return;
7577afa08cdSHyman Huang(黄勇)         }
7587afa08cdSHyman Huang(黄勇)     } else {
7597afa08cdSHyman Huang(黄勇)         sample_pages = DIRTYRATE_DEFAULT_SAMPLE_PAGES;
7607afa08cdSHyman Huang(黄勇)     }
7617afa08cdSHyman Huang(黄勇) 
7624c437254SChuan Zheng     /*
7630e21bf24SHyman Huang(黄勇)      * dirty ring mode only works when kvm dirty ring is enabled.
764826b8bc8SHyman Huang(黄勇)      * on the contrary, dirty bitmap mode is not.
7650e21bf24SHyman Huang(黄勇)      */
766826b8bc8SHyman Huang(黄勇)     if (((mode == DIRTY_RATE_MEASURE_MODE_DIRTY_RING) &&
767826b8bc8SHyman Huang(黄勇)         !kvm_dirty_ring_enabled()) ||
768826b8bc8SHyman Huang(黄勇)         ((mode == DIRTY_RATE_MEASURE_MODE_DIRTY_BITMAP) &&
769826b8bc8SHyman Huang(黄勇)          kvm_dirty_ring_enabled())) {
770826b8bc8SHyman Huang(黄勇)         error_setg(errp, "mode %s is not enabled, use other method instead.",
771826b8bc8SHyman Huang(黄勇)                          DirtyRateMeasureMode_str(mode));
7720e21bf24SHyman Huang(黄勇)          return;
7730e21bf24SHyman Huang(黄勇)     }
7740e21bf24SHyman Huang(黄勇) 
7750e21bf24SHyman Huang(黄勇)     /*
7764c437254SChuan Zheng      * Init calculation state as unstarted.
7774c437254SChuan Zheng      */
7784c437254SChuan Zheng     ret = dirtyrate_set_state(&CalculatingState, CalculatingState,
7794c437254SChuan Zheng                               DIRTY_RATE_STATUS_UNSTARTED);
7804c437254SChuan Zheng     if (ret == -1) {
7814c437254SChuan Zheng         error_setg(errp, "init dirty rate calculation state failed.");
7824c437254SChuan Zheng         return;
7834c437254SChuan Zheng     }
7844c437254SChuan Zheng 
7854c437254SChuan Zheng     config.sample_period_seconds = calc_time;
7867afa08cdSHyman Huang(黄勇)     config.sample_pages_per_gigabytes = sample_pages;
7870e21bf24SHyman Huang(黄勇)     config.mode = mode;
7880e21bf24SHyman Huang(黄勇) 
7890e21bf24SHyman Huang(黄勇)     cleanup_dirtyrate_stat(config);
7900e21bf24SHyman Huang(黄勇) 
7910e21bf24SHyman Huang(黄勇)     /*
7920e21bf24SHyman Huang(黄勇)      * update dirty rate mode so that we can figure out what mode has
7930e21bf24SHyman Huang(黄勇)      * been used in last calculation
7940e21bf24SHyman Huang(黄勇)      **/
7950e21bf24SHyman Huang(黄勇)     dirtyrate_mode = mode;
7969865d0f6SHyman Huang(黄勇) 
7979865d0f6SHyman Huang(黄勇)     start_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME) / 1000;
7989865d0f6SHyman Huang(黄勇)     init_dirtyrate_stat(start_time, config);
7999865d0f6SHyman Huang(黄勇) 
8004c437254SChuan Zheng     qemu_thread_create(&thread, "get_dirtyrate", get_dirtyrate_thread,
8014c437254SChuan Zheng                        (void *)&config, QEMU_THREAD_DETACHED);
8024c437254SChuan Zheng }
8034c437254SChuan Zheng 
8044c437254SChuan Zheng struct DirtyRateInfo *qmp_query_dirty_rate(Error **errp)
8054c437254SChuan Zheng {
8064c437254SChuan Zheng     return query_dirty_rate_info();
8074c437254SChuan Zheng }
808a4a571d9SPeter Xu 
809a4a571d9SPeter Xu void hmp_info_dirty_rate(Monitor *mon, const QDict *qdict)
810a4a571d9SPeter Xu {
811a4a571d9SPeter Xu     DirtyRateInfo *info = query_dirty_rate_info();
812a4a571d9SPeter Xu 
813a4a571d9SPeter Xu     monitor_printf(mon, "Status: %s\n",
814a4a571d9SPeter Xu                    DirtyRateStatus_str(info->status));
815a4a571d9SPeter Xu     monitor_printf(mon, "Start Time: %"PRIi64" (ms)\n",
816a4a571d9SPeter Xu                    info->start_time);
817bd9510d3SZhenzhong Duan     if (info->mode == DIRTY_RATE_MEASURE_MODE_PAGE_SAMPLING) {
818a4a571d9SPeter Xu         monitor_printf(mon, "Sample Pages: %"PRIu64" (per GB)\n",
819a4a571d9SPeter Xu                        info->sample_pages);
820bd9510d3SZhenzhong Duan     }
821a4a571d9SPeter Xu     monitor_printf(mon, "Period: %"PRIi64" (sec)\n",
822a4a571d9SPeter Xu                    info->calc_time);
8230e21bf24SHyman Huang(黄勇)     monitor_printf(mon, "Mode: %s\n",
8240e21bf24SHyman Huang(黄勇)                    DirtyRateMeasureMode_str(info->mode));
825a4a571d9SPeter Xu     monitor_printf(mon, "Dirty rate: ");
826a4a571d9SPeter Xu     if (info->has_dirty_rate) {
827a4a571d9SPeter Xu         monitor_printf(mon, "%"PRIi64" (MB/s)\n", info->dirty_rate);
8280e21bf24SHyman Huang(黄勇)         if (info->has_vcpu_dirty_rate) {
8290e21bf24SHyman Huang(黄勇)             DirtyRateVcpuList *rate, *head = info->vcpu_dirty_rate;
8300e21bf24SHyman Huang(黄勇)             for (rate = head; rate != NULL; rate = rate->next) {
8310e21bf24SHyman Huang(黄勇)                 monitor_printf(mon, "vcpu[%"PRIi64"], Dirty rate: %"PRIi64
8320e21bf24SHyman Huang(黄勇)                                " (MB/s)\n", rate->value->id,
8330e21bf24SHyman Huang(黄勇)                                rate->value->dirty_rate);
8340e21bf24SHyman Huang(黄勇)             }
8350e21bf24SHyman Huang(黄勇)         }
836a4a571d9SPeter Xu     } else {
837a4a571d9SPeter Xu         monitor_printf(mon, "(not ready)\n");
838a4a571d9SPeter Xu     }
8390e21bf24SHyman Huang(黄勇) 
8400e21bf24SHyman Huang(黄勇)     qapi_free_DirtyRateVcpuList(info->vcpu_dirty_rate);
841a4a571d9SPeter Xu     g_free(info);
842a4a571d9SPeter Xu }
843a4a571d9SPeter Xu 
844a4a571d9SPeter Xu void hmp_calc_dirty_rate(Monitor *mon, const QDict *qdict)
845a4a571d9SPeter Xu {
846a4a571d9SPeter Xu     int64_t sec = qdict_get_try_int(qdict, "second", 0);
847a4a571d9SPeter Xu     int64_t sample_pages = qdict_get_try_int(qdict, "sample_pages_per_GB", -1);
848a4a571d9SPeter Xu     bool has_sample_pages = (sample_pages != -1);
8490e21bf24SHyman Huang(黄勇)     bool dirty_ring = qdict_get_try_bool(qdict, "dirty_ring", false);
850826b8bc8SHyman Huang(黄勇)     bool dirty_bitmap = qdict_get_try_bool(qdict, "dirty_bitmap", false);
851826b8bc8SHyman Huang(黄勇)     DirtyRateMeasureMode mode = DIRTY_RATE_MEASURE_MODE_PAGE_SAMPLING;
852a4a571d9SPeter Xu     Error *err = NULL;
853a4a571d9SPeter Xu 
854a4a571d9SPeter Xu     if (!sec) {
855a4a571d9SPeter Xu         monitor_printf(mon, "Incorrect period length specified!\n");
856a4a571d9SPeter Xu         return;
857a4a571d9SPeter Xu     }
858a4a571d9SPeter Xu 
859826b8bc8SHyman Huang(黄勇)     if (dirty_ring && dirty_bitmap) {
860826b8bc8SHyman Huang(黄勇)         monitor_printf(mon, "Either dirty ring or dirty bitmap "
861826b8bc8SHyman Huang(黄勇)                        "can be specified!\n");
862826b8bc8SHyman Huang(黄勇)         return;
863826b8bc8SHyman Huang(黄勇)     }
864826b8bc8SHyman Huang(黄勇) 
865826b8bc8SHyman Huang(黄勇)     if (dirty_bitmap) {
866826b8bc8SHyman Huang(黄勇)         mode = DIRTY_RATE_MEASURE_MODE_DIRTY_BITMAP;
867826b8bc8SHyman Huang(黄勇)     } else if (dirty_ring) {
868826b8bc8SHyman Huang(黄勇)         mode = DIRTY_RATE_MEASURE_MODE_DIRTY_RING;
869826b8bc8SHyman Huang(黄勇)     }
870826b8bc8SHyman Huang(黄勇) 
8710e21bf24SHyman Huang(黄勇)     qmp_calc_dirty_rate(sec, has_sample_pages, sample_pages, true,
8720e21bf24SHyman Huang(黄勇)                         mode, &err);
873a4a571d9SPeter Xu     if (err) {
874a4a571d9SPeter Xu         hmp_handle_error(mon, err);
875a4a571d9SPeter Xu         return;
876a4a571d9SPeter Xu     }
877a4a571d9SPeter Xu 
878a4a571d9SPeter Xu     monitor_printf(mon, "Starting dirty rate measurement with period %"PRIi64
879a4a571d9SPeter Xu                    " seconds\n", sec);
880a4a571d9SPeter Xu     monitor_printf(mon, "[Please use 'info dirty_rate' to check results]\n");
881a4a571d9SPeter Xu }
882