qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

dirtyrate.c (24820B)


      1 /*
      2  * Dirtyrate implement code
      3  *
      4  * Copyright (c) 2020 HUAWEI TECHNOLOGIES CO.,LTD.
      5  *
      6  * Authors:
      7  *  Chuan Zheng <zhengchuan@huawei.com>
      8  *
      9  * This work is licensed under the terms of the GNU GPL, version 2 or later.
     10  * See the COPYING file in the top-level directory.
     11  */
     12 
     13 #include "qemu/osdep.h"
     14 #include <zlib.h>
     15 #include "qapi/error.h"
     16 #include "cpu.h"
     17 #include "exec/ramblock.h"
     18 #include "exec/ram_addr.h"
     19 #include "qemu/rcu_queue.h"
     20 #include "qemu/main-loop.h"
     21 #include "qapi/qapi-commands-migration.h"
     22 #include "ram.h"
     23 #include "trace.h"
     24 #include "dirtyrate.h"
     25 #include "monitor/hmp.h"
     26 #include "monitor/monitor.h"
     27 #include "qapi/qmp/qdict.h"
     28 #include "sysemu/kvm.h"
     29 #include "sysemu/runstate.h"
     30 #include "exec/memory.h"
     31 
     32 /*
     33  * total_dirty_pages is procted by BQL and is used
     34  * to stat dirty pages during the period of two
     35  * memory_global_dirty_log_sync
     36  */
     37 uint64_t total_dirty_pages;
     38 
     39 typedef struct DirtyPageRecord {
     40     uint64_t start_pages;
     41     uint64_t end_pages;
     42 } DirtyPageRecord;
     43 
     44 static int CalculatingState = DIRTY_RATE_STATUS_UNSTARTED;
     45 static struct DirtyRateStat DirtyStat;
     46 static DirtyRateMeasureMode dirtyrate_mode =
     47                 DIRTY_RATE_MEASURE_MODE_PAGE_SAMPLING;
     48 
     49 static int64_t dirty_stat_wait(int64_t msec, int64_t initial_time)
     50 {
     51     int64_t current_time;
     52 
     53     current_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
     54     if ((current_time - initial_time) >= msec) {
     55         msec = current_time - initial_time;
     56     } else {
     57         g_usleep((msec + initial_time - current_time) * 1000);
     58     }
     59 
     60     return msec;
     61 }
     62 
     63 static inline void record_dirtypages(DirtyPageRecord *dirty_pages,
     64                                      CPUState *cpu, bool start)
     65 {
     66     if (start) {
     67         dirty_pages[cpu->cpu_index].start_pages = cpu->dirty_pages;
     68     } else {
     69         dirty_pages[cpu->cpu_index].end_pages = cpu->dirty_pages;
     70     }
     71 }
     72 
     73 static int64_t do_calculate_dirtyrate(DirtyPageRecord dirty_pages,
     74                                       int64_t calc_time_ms)
     75 {
     76     uint64_t memory_size_MB;
     77     uint64_t increased_dirty_pages =
     78         dirty_pages.end_pages - dirty_pages.start_pages;
     79 
     80     memory_size_MB = (increased_dirty_pages * TARGET_PAGE_SIZE) >> 20;
     81 
     82     return memory_size_MB * 1000 / calc_time_ms;
     83 }
     84 
     85 void global_dirty_log_change(unsigned int flag, bool start)
     86 {
     87     qemu_mutex_lock_iothread();
     88     if (start) {
     89         memory_global_dirty_log_start(flag);
     90     } else {
     91         memory_global_dirty_log_stop(flag);
     92     }
     93     qemu_mutex_unlock_iothread();
     94 }
     95 
     96 /*
     97  * global_dirty_log_sync
     98  * 1. sync dirty log from kvm
     99  * 2. stop dirty tracking if needed.
    100  */
    101 static void global_dirty_log_sync(unsigned int flag, bool one_shot)
    102 {
    103     qemu_mutex_lock_iothread();
    104     memory_global_dirty_log_sync();
    105     if (one_shot) {
    106         memory_global_dirty_log_stop(flag);
    107     }
    108     qemu_mutex_unlock_iothread();
    109 }
    110 
    111 static DirtyPageRecord *vcpu_dirty_stat_alloc(VcpuStat *stat)
    112 {
    113     CPUState *cpu;
    114     DirtyPageRecord *records;
    115     int nvcpu = 0;
    116 
    117     CPU_FOREACH(cpu) {
    118         nvcpu++;
    119     }
    120 
    121     stat->nvcpu = nvcpu;
    122     stat->rates = g_new0(DirtyRateVcpu, nvcpu);
    123 
    124     records = g_new0(DirtyPageRecord, nvcpu);
    125 
    126     return records;
    127 }
    128 
    129 static void vcpu_dirty_stat_collect(VcpuStat *stat,
    130                                     DirtyPageRecord *records,
    131                                     bool start)
    132 {
    133     CPUState *cpu;
    134 
    135     CPU_FOREACH(cpu) {
    136         record_dirtypages(records, cpu, start);
    137     }
    138 }
    139 
    140 int64_t vcpu_calculate_dirtyrate(int64_t calc_time_ms,
    141                                  VcpuStat *stat,
    142                                  unsigned int flag,
    143                                  bool one_shot)
    144 {
    145     DirtyPageRecord *records;
    146     int64_t init_time_ms;
    147     int64_t duration;
    148     int64_t dirtyrate;
    149     int i = 0;
    150     unsigned int gen_id;
    151 
    152 retry:
    153     init_time_ms = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
    154 
    155     cpu_list_lock();
    156     gen_id = cpu_list_generation_id_get();
    157     records = vcpu_dirty_stat_alloc(stat);
    158     vcpu_dirty_stat_collect(stat, records, true);
    159     cpu_list_unlock();
    160 
    161     duration = dirty_stat_wait(calc_time_ms, init_time_ms);
    162 
    163     global_dirty_log_sync(flag, one_shot);
    164 
    165     cpu_list_lock();
    166     if (gen_id != cpu_list_generation_id_get()) {
    167         g_free(records);
    168         g_free(stat->rates);
    169         cpu_list_unlock();
    170         goto retry;
    171     }
    172     vcpu_dirty_stat_collect(stat, records, false);
    173     cpu_list_unlock();
    174 
    175     for (i = 0; i < stat->nvcpu; i++) {
    176         dirtyrate = do_calculate_dirtyrate(records[i], duration);
    177 
    178         stat->rates[i].id = i;
    179         stat->rates[i].dirty_rate = dirtyrate;
    180 
    181         trace_dirtyrate_do_calculate_vcpu(i, dirtyrate);
    182     }
    183 
    184     g_free(records);
    185 
    186     return duration;
    187 }
    188 
    189 static bool is_sample_period_valid(int64_t sec)
    190 {
    191     if (sec < MIN_FETCH_DIRTYRATE_TIME_SEC ||
    192         sec > MAX_FETCH_DIRTYRATE_TIME_SEC) {
    193         return false;
    194     }
    195 
    196     return true;
    197 }
    198 
    199 static bool is_sample_pages_valid(int64_t pages)
    200 {
    201     return pages >= MIN_SAMPLE_PAGE_COUNT &&
    202            pages <= MAX_SAMPLE_PAGE_COUNT;
    203 }
    204 
    205 static int dirtyrate_set_state(int *state, int old_state, int new_state)
    206 {
    207     assert(new_state < DIRTY_RATE_STATUS__MAX);
    208     trace_dirtyrate_set_state(DirtyRateStatus_str(new_state));
    209     if (qatomic_cmpxchg(state, old_state, new_state) == old_state) {
    210         return 0;
    211     } else {
    212         return -1;
    213     }
    214 }
    215 
    216 static struct DirtyRateInfo *query_dirty_rate_info(void)
    217 {
    218     int i;
    219     int64_t dirty_rate = DirtyStat.dirty_rate;
    220     struct DirtyRateInfo *info = g_new0(DirtyRateInfo, 1);
    221     DirtyRateVcpuList *head = NULL, **tail = &head;
    222 
    223     info->status = CalculatingState;
    224     info->start_time = DirtyStat.start_time;
    225     info->calc_time = DirtyStat.calc_time;
    226     info->sample_pages = DirtyStat.sample_pages;
    227     info->mode = dirtyrate_mode;
    228 
    229     if (qatomic_read(&CalculatingState) == DIRTY_RATE_STATUS_MEASURED) {
    230         info->has_dirty_rate = true;
    231         info->dirty_rate = dirty_rate;
    232 
    233         if (dirtyrate_mode == DIRTY_RATE_MEASURE_MODE_DIRTY_RING) {
    234             /*
    235              * set sample_pages with 0 to indicate page sampling
    236              * isn't enabled
    237              **/
    238             info->sample_pages = 0;
    239             info->has_vcpu_dirty_rate = true;
    240             for (i = 0; i < DirtyStat.dirty_ring.nvcpu; i++) {
    241                 DirtyRateVcpu *rate = g_new0(DirtyRateVcpu, 1);
    242                 rate->id = DirtyStat.dirty_ring.rates[i].id;
    243                 rate->dirty_rate = DirtyStat.dirty_ring.rates[i].dirty_rate;
    244                 QAPI_LIST_APPEND(tail, rate);
    245             }
    246             info->vcpu_dirty_rate = head;
    247         }
    248 
    249         if (dirtyrate_mode == DIRTY_RATE_MEASURE_MODE_DIRTY_BITMAP) {
    250             info->sample_pages = 0;
    251         }
    252     }
    253 
    254     trace_query_dirty_rate_info(DirtyRateStatus_str(CalculatingState));
    255 
    256     return info;
    257 }
    258 
    259 static void init_dirtyrate_stat(int64_t start_time,
    260                                 struct DirtyRateConfig config)
    261 {
    262     DirtyStat.dirty_rate = -1;
    263     DirtyStat.start_time = start_time;
    264     DirtyStat.calc_time = config.sample_period_seconds;
    265     DirtyStat.sample_pages = config.sample_pages_per_gigabytes;
    266 
    267     switch (config.mode) {
    268     case DIRTY_RATE_MEASURE_MODE_PAGE_SAMPLING:
    269         DirtyStat.page_sampling.total_dirty_samples = 0;
    270         DirtyStat.page_sampling.total_sample_count = 0;
    271         DirtyStat.page_sampling.total_block_mem_MB = 0;
    272         break;
    273     case DIRTY_RATE_MEASURE_MODE_DIRTY_RING:
    274         DirtyStat.dirty_ring.nvcpu = -1;
    275         DirtyStat.dirty_ring.rates = NULL;
    276         break;
    277     default:
    278         break;
    279     }
    280 }
    281 
    282 static void cleanup_dirtyrate_stat(struct DirtyRateConfig config)
    283 {
    284     /* last calc-dirty-rate qmp use dirty ring mode */
    285     if (dirtyrate_mode == DIRTY_RATE_MEASURE_MODE_DIRTY_RING) {
    286         free(DirtyStat.dirty_ring.rates);
    287         DirtyStat.dirty_ring.rates = NULL;
    288     }
    289 }
    290 
    291 static void update_dirtyrate_stat(struct RamblockDirtyInfo *info)
    292 {
    293     DirtyStat.page_sampling.total_dirty_samples += info->sample_dirty_count;
    294     DirtyStat.page_sampling.total_sample_count += info->sample_pages_count;
    295     /* size of total pages in MB */
    296     DirtyStat.page_sampling.total_block_mem_MB += (info->ramblock_pages *
    297                                                    TARGET_PAGE_SIZE) >> 20;
    298 }
    299 
    300 static void update_dirtyrate(uint64_t msec)
    301 {
    302     uint64_t dirtyrate;
    303     uint64_t total_dirty_samples = DirtyStat.page_sampling.total_dirty_samples;
    304     uint64_t total_sample_count = DirtyStat.page_sampling.total_sample_count;
    305     uint64_t total_block_mem_MB = DirtyStat.page_sampling.total_block_mem_MB;
    306 
    307     dirtyrate = total_dirty_samples * total_block_mem_MB *
    308                 1000 / (total_sample_count * msec);
    309 
    310     DirtyStat.dirty_rate = dirtyrate;
    311 }
    312 
    313 /*
    314  * get hash result for the sampled memory with length of TARGET_PAGE_SIZE
    315  * in ramblock, which starts from ramblock base address.
    316  */
    317 static uint32_t get_ramblock_vfn_hash(struct RamblockDirtyInfo *info,
    318                                       uint64_t vfn)
    319 {
    320     uint32_t crc;
    321 
    322     crc = crc32(0, (info->ramblock_addr +
    323                 vfn * TARGET_PAGE_SIZE), TARGET_PAGE_SIZE);
    324 
    325     trace_get_ramblock_vfn_hash(info->idstr, vfn, crc);
    326     return crc;
    327 }
    328 
    329 static bool save_ramblock_hash(struct RamblockDirtyInfo *info)
    330 {
    331     unsigned int sample_pages_count;
    332     int i;
    333     GRand *rand;
    334 
    335     sample_pages_count = info->sample_pages_count;
    336 
    337     /* ramblock size less than one page, return success to skip this ramblock */
    338     if (unlikely(info->ramblock_pages == 0 || sample_pages_count == 0)) {
    339         return true;
    340     }
    341 
    342     info->hash_result = g_try_malloc0_n(sample_pages_count,
    343                                         sizeof(uint32_t));
    344     if (!info->hash_result) {
    345         return false;
    346     }
    347 
    348     info->sample_page_vfn = g_try_malloc0_n(sample_pages_count,
    349                                             sizeof(uint64_t));
    350     if (!info->sample_page_vfn) {
    351         g_free(info->hash_result);
    352         return false;
    353     }
    354 
    355     rand  = g_rand_new();
    356     for (i = 0; i < sample_pages_count; i++) {
    357         info->sample_page_vfn[i] = g_rand_int_range(rand, 0,
    358                                                     info->ramblock_pages - 1);
    359         info->hash_result[i] = get_ramblock_vfn_hash(info,
    360                                                      info->sample_page_vfn[i]);
    361     }
    362     g_rand_free(rand);
    363 
    364     return true;
    365 }
    366 
    367 static void get_ramblock_dirty_info(RAMBlock *block,
    368                                     struct RamblockDirtyInfo *info,
    369                                     struct DirtyRateConfig *config)
    370 {
    371     uint64_t sample_pages_per_gigabytes = config->sample_pages_per_gigabytes;
    372 
    373     /* Right shift 30 bits to calc ramblock size in GB */
    374     info->sample_pages_count = (qemu_ram_get_used_length(block) *
    375                                 sample_pages_per_gigabytes) >> 30;
    376     /* Right shift TARGET_PAGE_BITS to calc page count */
    377     info->ramblock_pages = qemu_ram_get_used_length(block) >>
    378                            TARGET_PAGE_BITS;
    379     info->ramblock_addr = qemu_ram_get_host_addr(block);
    380     strcpy(info->idstr, qemu_ram_get_idstr(block));
    381 }
    382 
    383 static void free_ramblock_dirty_info(struct RamblockDirtyInfo *infos, int count)
    384 {
    385     int i;
    386 
    387     if (!infos) {
    388         return;
    389     }
    390 
    391     for (i = 0; i < count; i++) {
    392         g_free(infos[i].sample_page_vfn);
    393         g_free(infos[i].hash_result);
    394     }
    395     g_free(infos);
    396 }
    397 
    398 static bool skip_sample_ramblock(RAMBlock *block)
    399 {
    400     /*
    401      * Sample only blocks larger than MIN_RAMBLOCK_SIZE.
    402      */
    403     if (qemu_ram_get_used_length(block) < (MIN_RAMBLOCK_SIZE << 10)) {
    404         trace_skip_sample_ramblock(block->idstr,
    405                                    qemu_ram_get_used_length(block));
    406         return true;
    407     }
    408 
    409     return false;
    410 }
    411 
    412 static bool record_ramblock_hash_info(struct RamblockDirtyInfo **block_dinfo,
    413                                       struct DirtyRateConfig config,
    414                                       int *block_count)
    415 {
    416     struct RamblockDirtyInfo *info = NULL;
    417     struct RamblockDirtyInfo *dinfo = NULL;
    418     RAMBlock *block = NULL;
    419     int total_count = 0;
    420     int index = 0;
    421     bool ret = false;
    422 
    423     RAMBLOCK_FOREACH_MIGRATABLE(block) {
    424         if (skip_sample_ramblock(block)) {
    425             continue;
    426         }
    427         total_count++;
    428     }
    429 
    430     dinfo = g_try_malloc0_n(total_count, sizeof(struct RamblockDirtyInfo));
    431     if (dinfo == NULL) {
    432         goto out;
    433     }
    434 
    435     RAMBLOCK_FOREACH_MIGRATABLE(block) {
    436         if (skip_sample_ramblock(block)) {
    437             continue;
    438         }
    439         if (index >= total_count) {
    440             break;
    441         }
    442         info = &dinfo[index];
    443         get_ramblock_dirty_info(block, info, &config);
    444         if (!save_ramblock_hash(info)) {
    445             goto out;
    446         }
    447         index++;
    448     }
    449     ret = true;
    450 
    451 out:
    452     *block_count = index;
    453     *block_dinfo = dinfo;
    454     return ret;
    455 }
    456 
    457 static void calc_page_dirty_rate(struct RamblockDirtyInfo *info)
    458 {
    459     uint32_t crc;
    460     int i;
    461 
    462     for (i = 0; i < info->sample_pages_count; i++) {
    463         crc = get_ramblock_vfn_hash(info, info->sample_page_vfn[i]);
    464         if (crc != info->hash_result[i]) {
    465             trace_calc_page_dirty_rate(info->idstr, crc, info->hash_result[i]);
    466             info->sample_dirty_count++;
    467         }
    468     }
    469 }
    470 
    471 static struct RamblockDirtyInfo *
    472 find_block_matched(RAMBlock *block, int count,
    473                   struct RamblockDirtyInfo *infos)
    474 {
    475     int i;
    476     struct RamblockDirtyInfo *matched;
    477 
    478     for (i = 0; i < count; i++) {
    479         if (!strcmp(infos[i].idstr, qemu_ram_get_idstr(block))) {
    480             break;
    481         }
    482     }
    483 
    484     if (i == count) {
    485         return NULL;
    486     }
    487 
    488     if (infos[i].ramblock_addr != qemu_ram_get_host_addr(block) ||
    489         infos[i].ramblock_pages !=
    490             (qemu_ram_get_used_length(block) >> TARGET_PAGE_BITS)) {
    491         trace_find_page_matched(block->idstr);
    492         return NULL;
    493     }
    494 
    495     matched = &infos[i];
    496 
    497     return matched;
    498 }
    499 
    500 static bool compare_page_hash_info(struct RamblockDirtyInfo *info,
    501                                   int block_count)
    502 {
    503     struct RamblockDirtyInfo *block_dinfo = NULL;
    504     RAMBlock *block = NULL;
    505 
    506     RAMBLOCK_FOREACH_MIGRATABLE(block) {
    507         if (skip_sample_ramblock(block)) {
    508             continue;
    509         }
    510         block_dinfo = find_block_matched(block, block_count, info);
    511         if (block_dinfo == NULL) {
    512             continue;
    513         }
    514         calc_page_dirty_rate(block_dinfo);
    515         update_dirtyrate_stat(block_dinfo);
    516     }
    517 
    518     if (DirtyStat.page_sampling.total_sample_count == 0) {
    519         return false;
    520     }
    521 
    522     return true;
    523 }
    524 
    525 static inline void record_dirtypages_bitmap(DirtyPageRecord *dirty_pages,
    526                                             bool start)
    527 {
    528     if (start) {
    529         dirty_pages->start_pages = total_dirty_pages;
    530     } else {
    531         dirty_pages->end_pages = total_dirty_pages;
    532     }
    533 }
    534 
    535 static inline void dirtyrate_manual_reset_protect(void)
    536 {
    537     RAMBlock *block = NULL;
    538 
    539     WITH_RCU_READ_LOCK_GUARD() {
    540         RAMBLOCK_FOREACH_MIGRATABLE(block) {
    541             memory_region_clear_dirty_bitmap(block->mr, 0,
    542                                              block->used_length);
    543         }
    544     }
    545 }
    546 
    547 static void calculate_dirtyrate_dirty_bitmap(struct DirtyRateConfig config)
    548 {
    549     int64_t msec = 0;
    550     int64_t start_time;
    551     DirtyPageRecord dirty_pages;
    552 
    553     qemu_mutex_lock_iothread();
    554     memory_global_dirty_log_start(GLOBAL_DIRTY_DIRTY_RATE);
    555 
    556     /*
    557      * 1'round of log sync may return all 1 bits with
    558      * KVM_DIRTY_LOG_INITIALLY_SET enable
    559      * skip it unconditionally and start dirty tracking
    560      * from 2'round of log sync
    561      */
    562     memory_global_dirty_log_sync();
    563 
    564     /*
    565      * reset page protect manually and unconditionally.
    566      * this make sure kvm dirty log be cleared if
    567      * KVM_DIRTY_LOG_MANUAL_PROTECT_ENABLE cap is enabled.
    568      */
    569     dirtyrate_manual_reset_protect();
    570     qemu_mutex_unlock_iothread();
    571 
    572     record_dirtypages_bitmap(&dirty_pages, true);
    573 
    574     start_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
    575     DirtyStat.start_time = start_time / 1000;
    576 
    577     msec = config.sample_period_seconds * 1000;
    578     msec = dirty_stat_wait(msec, start_time);
    579     DirtyStat.calc_time = msec / 1000;
    580 
    581     /*
    582      * do two things.
    583      * 1. fetch dirty bitmap from kvm
    584      * 2. stop dirty tracking
    585      */
    586     global_dirty_log_sync(GLOBAL_DIRTY_DIRTY_RATE, true);
    587 
    588     record_dirtypages_bitmap(&dirty_pages, false);
    589 
    590     DirtyStat.dirty_rate = do_calculate_dirtyrate(dirty_pages, msec);
    591 }
    592 
    593 static void calculate_dirtyrate_dirty_ring(struct DirtyRateConfig config)
    594 {
    595     int64_t duration;
    596     uint64_t dirtyrate = 0;
    597     uint64_t dirtyrate_sum = 0;
    598     int i = 0;
    599 
    600     /* start log sync */
    601     global_dirty_log_change(GLOBAL_DIRTY_DIRTY_RATE, true);
    602 
    603     DirtyStat.start_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME) / 1000;
    604 
    605     /* calculate vcpu dirtyrate */
    606     duration = vcpu_calculate_dirtyrate(config.sample_period_seconds * 1000,
    607                                         &DirtyStat.dirty_ring,
    608                                         GLOBAL_DIRTY_DIRTY_RATE,
    609                                         true);
    610 
    611     DirtyStat.calc_time = duration / 1000;
    612 
    613     /* calculate vm dirtyrate */
    614     for (i = 0; i < DirtyStat.dirty_ring.nvcpu; i++) {
    615         dirtyrate = DirtyStat.dirty_ring.rates[i].dirty_rate;
    616         DirtyStat.dirty_ring.rates[i].dirty_rate = dirtyrate;
    617         dirtyrate_sum += dirtyrate;
    618     }
    619 
    620     DirtyStat.dirty_rate = dirtyrate_sum;
    621 }
    622 
    623 static void calculate_dirtyrate_sample_vm(struct DirtyRateConfig config)
    624 {
    625     struct RamblockDirtyInfo *block_dinfo = NULL;
    626     int block_count = 0;
    627     int64_t msec = 0;
    628     int64_t initial_time;
    629 
    630     rcu_read_lock();
    631     initial_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
    632     if (!record_ramblock_hash_info(&block_dinfo, config, &block_count)) {
    633         goto out;
    634     }
    635     rcu_read_unlock();
    636 
    637     msec = config.sample_period_seconds * 1000;
    638     msec = dirty_stat_wait(msec, initial_time);
    639     DirtyStat.start_time = initial_time / 1000;
    640     DirtyStat.calc_time = msec / 1000;
    641 
    642     rcu_read_lock();
    643     if (!compare_page_hash_info(block_dinfo, block_count)) {
    644         goto out;
    645     }
    646 
    647     update_dirtyrate(msec);
    648 
    649 out:
    650     rcu_read_unlock();
    651     free_ramblock_dirty_info(block_dinfo, block_count);
    652 }
    653 
    654 static void calculate_dirtyrate(struct DirtyRateConfig config)
    655 {
    656     if (config.mode == DIRTY_RATE_MEASURE_MODE_DIRTY_BITMAP) {
    657         calculate_dirtyrate_dirty_bitmap(config);
    658     } else if (config.mode == DIRTY_RATE_MEASURE_MODE_DIRTY_RING) {
    659         calculate_dirtyrate_dirty_ring(config);
    660     } else {
    661         calculate_dirtyrate_sample_vm(config);
    662     }
    663 
    664     trace_dirtyrate_calculate(DirtyStat.dirty_rate);
    665 }
    666 
    667 void *get_dirtyrate_thread(void *arg)
    668 {
    669     struct DirtyRateConfig config = *(struct DirtyRateConfig *)arg;
    670     int ret;
    671     rcu_register_thread();
    672 
    673     ret = dirtyrate_set_state(&CalculatingState, DIRTY_RATE_STATUS_UNSTARTED,
    674                               DIRTY_RATE_STATUS_MEASURING);
    675     if (ret == -1) {
    676         error_report("change dirtyrate state failed.");
    677         return NULL;
    678     }
    679 
    680     calculate_dirtyrate(config);
    681 
    682     ret = dirtyrate_set_state(&CalculatingState, DIRTY_RATE_STATUS_MEASURING,
    683                               DIRTY_RATE_STATUS_MEASURED);
    684     if (ret == -1) {
    685         error_report("change dirtyrate state failed.");
    686     }
    687 
    688     rcu_unregister_thread();
    689     return NULL;
    690 }
    691 
    692 void qmp_calc_dirty_rate(int64_t calc_time,
    693                          bool has_sample_pages,
    694                          int64_t sample_pages,
    695                          bool has_mode,
    696                          DirtyRateMeasureMode mode,
    697                          Error **errp)
    698 {
    699     static struct DirtyRateConfig config;
    700     QemuThread thread;
    701     int ret;
    702     int64_t start_time;
    703 
    704     /*
    705      * If the dirty rate is already being measured, don't attempt to start.
    706      */
    707     if (qatomic_read(&CalculatingState) == DIRTY_RATE_STATUS_MEASURING) {
    708         error_setg(errp, "the dirty rate is already being measured.");
    709         return;
    710     }
    711 
    712     if (!is_sample_period_valid(calc_time)) {
    713         error_setg(errp, "calc-time is out of range[%d, %d].",
    714                          MIN_FETCH_DIRTYRATE_TIME_SEC,
    715                          MAX_FETCH_DIRTYRATE_TIME_SEC);
    716         return;
    717     }
    718 
    719     if (!has_mode) {
    720         mode =  DIRTY_RATE_MEASURE_MODE_PAGE_SAMPLING;
    721     }
    722 
    723     if (has_sample_pages && mode == DIRTY_RATE_MEASURE_MODE_DIRTY_RING) {
    724         error_setg(errp, "either sample-pages or dirty-ring can be specified.");
    725         return;
    726     }
    727 
    728     if (has_sample_pages) {
    729         if (!is_sample_pages_valid(sample_pages)) {
    730             error_setg(errp, "sample-pages is out of range[%d, %d].",
    731                             MIN_SAMPLE_PAGE_COUNT,
    732                             MAX_SAMPLE_PAGE_COUNT);
    733             return;
    734         }
    735     } else {
    736         sample_pages = DIRTYRATE_DEFAULT_SAMPLE_PAGES;
    737     }
    738 
    739     /*
    740      * dirty ring mode only works when kvm dirty ring is enabled.
    741      * on the contrary, dirty bitmap mode is not.
    742      */
    743     if (((mode == DIRTY_RATE_MEASURE_MODE_DIRTY_RING) &&
    744         !kvm_dirty_ring_enabled()) ||
    745         ((mode == DIRTY_RATE_MEASURE_MODE_DIRTY_BITMAP) &&
    746          kvm_dirty_ring_enabled())) {
    747         error_setg(errp, "mode %s is not enabled, use other method instead.",
    748                          DirtyRateMeasureMode_str(mode));
    749          return;
    750     }
    751 
    752     /*
    753      * Init calculation state as unstarted.
    754      */
    755     ret = dirtyrate_set_state(&CalculatingState, CalculatingState,
    756                               DIRTY_RATE_STATUS_UNSTARTED);
    757     if (ret == -1) {
    758         error_setg(errp, "init dirty rate calculation state failed.");
    759         return;
    760     }
    761 
    762     config.sample_period_seconds = calc_time;
    763     config.sample_pages_per_gigabytes = sample_pages;
    764     config.mode = mode;
    765 
    766     cleanup_dirtyrate_stat(config);
    767 
    768     /*
    769      * update dirty rate mode so that we can figure out what mode has
    770      * been used in last calculation
    771      **/
    772     dirtyrate_mode = mode;
    773 
    774     start_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME) / 1000;
    775     init_dirtyrate_stat(start_time, config);
    776 
    777     qemu_thread_create(&thread, "get_dirtyrate", get_dirtyrate_thread,
    778                        (void *)&config, QEMU_THREAD_DETACHED);
    779 }
    780 
    781 struct DirtyRateInfo *qmp_query_dirty_rate(Error **errp)
    782 {
    783     return query_dirty_rate_info();
    784 }
    785 
    786 void hmp_info_dirty_rate(Monitor *mon, const QDict *qdict)
    787 {
    788     DirtyRateInfo *info = query_dirty_rate_info();
    789 
    790     monitor_printf(mon, "Status: %s\n",
    791                    DirtyRateStatus_str(info->status));
    792     monitor_printf(mon, "Start Time: %"PRIi64" (ms)\n",
    793                    info->start_time);
    794     monitor_printf(mon, "Sample Pages: %"PRIu64" (per GB)\n",
    795                    info->sample_pages);
    796     monitor_printf(mon, "Period: %"PRIi64" (sec)\n",
    797                    info->calc_time);
    798     monitor_printf(mon, "Mode: %s\n",
    799                    DirtyRateMeasureMode_str(info->mode));
    800     monitor_printf(mon, "Dirty rate: ");
    801     if (info->has_dirty_rate) {
    802         monitor_printf(mon, "%"PRIi64" (MB/s)\n", info->dirty_rate);
    803         if (info->has_vcpu_dirty_rate) {
    804             DirtyRateVcpuList *rate, *head = info->vcpu_dirty_rate;
    805             for (rate = head; rate != NULL; rate = rate->next) {
    806                 monitor_printf(mon, "vcpu[%"PRIi64"], Dirty rate: %"PRIi64
    807                                " (MB/s)\n", rate->value->id,
    808                                rate->value->dirty_rate);
    809             }
    810         }
    811     } else {
    812         monitor_printf(mon, "(not ready)\n");
    813     }
    814 
    815     qapi_free_DirtyRateVcpuList(info->vcpu_dirty_rate);
    816     g_free(info);
    817 }
    818 
    819 void hmp_calc_dirty_rate(Monitor *mon, const QDict *qdict)
    820 {
    821     int64_t sec = qdict_get_try_int(qdict, "second", 0);
    822     int64_t sample_pages = qdict_get_try_int(qdict, "sample_pages_per_GB", -1);
    823     bool has_sample_pages = (sample_pages != -1);
    824     bool dirty_ring = qdict_get_try_bool(qdict, "dirty_ring", false);
    825     bool dirty_bitmap = qdict_get_try_bool(qdict, "dirty_bitmap", false);
    826     DirtyRateMeasureMode mode = DIRTY_RATE_MEASURE_MODE_PAGE_SAMPLING;
    827     Error *err = NULL;
    828 
    829     if (!sec) {
    830         monitor_printf(mon, "Incorrect period length specified!\n");
    831         return;
    832     }
    833 
    834     if (dirty_ring && dirty_bitmap) {
    835         monitor_printf(mon, "Either dirty ring or dirty bitmap "
    836                        "can be specified!\n");
    837         return;
    838     }
    839 
    840     if (dirty_bitmap) {
    841         mode = DIRTY_RATE_MEASURE_MODE_DIRTY_BITMAP;
    842     } else if (dirty_ring) {
    843         mode = DIRTY_RATE_MEASURE_MODE_DIRTY_RING;
    844     }
    845 
    846     qmp_calc_dirty_rate(sec, has_sample_pages, sample_pages, true,
    847                         mode, &err);
    848     if (err) {
    849         hmp_handle_error(mon, err);
    850         return;
    851     }
    852 
    853     monitor_printf(mon, "Starting dirty rate measurement with period %"PRIi64
    854                    " seconds\n", sec);
    855     monitor_printf(mon, "[Please use 'info dirty_rate' to check results]\n");
    856 }