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