| /* |
| * Multifd zero page detection implementation. |
| * |
| * Copyright (c) 2024 Bytedance Inc |
| * |
| * Authors: |
| * Hao Xiang <hao.xiang@bytedance.com> |
| * |
| * This work is licensed under the terms of the GNU GPL, version 2 or later. |
| * See the COPYING file in the top-level directory. |
| */ |
| |
| #include "qemu/osdep.h" |
| #include "qemu/cutils.h" |
| #include "exec/ramblock.h" |
| #include "migration.h" |
| #include "migration-stats.h" |
| #include "multifd.h" |
| #include "options.h" |
| #include "ram.h" |
| |
| static bool multifd_zero_page_enabled(void) |
| { |
| return migrate_zero_page_detection() == ZERO_PAGE_DETECTION_MULTIFD; |
| } |
| |
| static void swap_page_offset(ram_addr_t *pages_offset, int a, int b) |
| { |
| ram_addr_t temp; |
| |
| if (a == b) { |
| return; |
| } |
| |
| temp = pages_offset[a]; |
| pages_offset[a] = pages_offset[b]; |
| pages_offset[b] = temp; |
| } |
| |
| /** |
| * multifd_send_zero_page_detect: Perform zero page detection on all pages. |
| * |
| * Sorts normal pages before zero pages in p->pages->offset and updates |
| * p->pages->normal_num. |
| * |
| * @param p A pointer to the send params. |
| */ |
| void multifd_send_zero_page_detect(MultiFDSendParams *p) |
| { |
| MultiFDPages_t *pages = &p->data->u.ram; |
| RAMBlock *rb = pages->block; |
| int i = 0; |
| int j = pages->num - 1; |
| |
| if (!multifd_zero_page_enabled()) { |
| pages->normal_num = pages->num; |
| goto out; |
| } |
| |
| /* |
| * Sort the page offset array by moving all normal pages to |
| * the left and all zero pages to the right of the array. |
| */ |
| while (i <= j) { |
| uint64_t offset = pages->offset[i]; |
| |
| if (!buffer_is_zero(rb->host + offset, multifd_ram_page_size())) { |
| i++; |
| continue; |
| } |
| |
| swap_page_offset(pages->offset, i, j); |
| ram_release_page(rb->idstr, offset); |
| j--; |
| } |
| |
| pages->normal_num = i; |
| |
| out: |
| stat64_add(&mig_stats.normal_pages, pages->normal_num); |
| stat64_add(&mig_stats.zero_pages, pages->num - pages->normal_num); |
| } |
| |
| void multifd_recv_zero_page_process(MultiFDRecvParams *p) |
| { |
| for (int i = 0; i < p->zero_num; i++) { |
| void *page = p->host + p->zero[i]; |
| if (ramblock_recv_bitmap_test_byte_offset(p->block, p->zero[i])) { |
| memset(page, 0, multifd_ram_page_size()); |
| } else { |
| ramblock_recv_bitmap_set_offset(p->block, p->zero[i]); |
| } |
| } |
| } |