/* * Multifd zero page detection implementation. * * Copyright (c) 2024 Bytedance Inc * * Authors: * Hao Xiang * * 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]); } } }