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_malloc0(sizeof(DirtyRateVcpu) * nvcpu); 123 124 records = g_malloc0(sizeof(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 } 857