1 /*
2  * Multifd zero page detection implementation.
3  *
4  * Copyright (c) 2024 Bytedance Inc
5  *
6  * Authors:
7  *  Hao Xiang <hao.xiang@bytedance.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 "qemu/cutils.h"
15 #include "exec/ramblock.h"
16 #include "migration.h"
17 #include "migration-stats.h"
18 #include "multifd.h"
19 #include "options.h"
20 #include "ram.h"
21 
22 static bool multifd_zero_page_enabled(void)
23 {
24     return migrate_zero_page_detection() == ZERO_PAGE_DETECTION_MULTIFD;
25 }
26 
27 static void swap_page_offset(ram_addr_t *pages_offset, int a, int b)
28 {
29     ram_addr_t temp;
30 
31     if (a == b) {
32         return;
33     }
34 
35     temp = pages_offset[a];
36     pages_offset[a] = pages_offset[b];
37     pages_offset[b] = temp;
38 }
39 
40 /**
41  * multifd_send_zero_page_detect: Perform zero page detection on all pages.
42  *
43  * Sorts normal pages before zero pages in p->pages->offset and updates
44  * p->pages->normal_num.
45  *
46  * @param p A pointer to the send params.
47  */
48 void multifd_send_zero_page_detect(MultiFDSendParams *p)
49 {
50     MultiFDPages_t *pages = &p->data->u.ram;
51     RAMBlock *rb = pages->block;
52     int i = 0;
53     int j = pages->num - 1;
54 
55     if (!multifd_zero_page_enabled()) {
56         pages->normal_num = pages->num;
57         goto out;
58     }
59 
60     /*
61      * Sort the page offset array by moving all normal pages to
62      * the left and all zero pages to the right of the array.
63      */
64     while (i <= j) {
65         uint64_t offset = pages->offset[i];
66 
67         if (!buffer_is_zero(rb->host + offset, multifd_ram_page_size())) {
68             i++;
69             continue;
70         }
71 
72         swap_page_offset(pages->offset, i, j);
73         ram_release_page(rb->idstr, offset);
74         j--;
75     }
76 
77     pages->normal_num = i;
78 
79 out:
80     stat64_add(&mig_stats.normal_pages, pages->normal_num);
81     stat64_add(&mig_stats.zero_pages, pages->num - pages->normal_num);
82 }
83 
84 void multifd_recv_zero_page_process(MultiFDRecvParams *p)
85 {
86     for (int i = 0; i < p->zero_num; i++) {
87         void *page = p->host + p->zero[i];
88         if (ramblock_recv_bitmap_test_byte_offset(p->block, p->zero[i])) {
89             memset(page, 0, multifd_ram_page_size());
90         } else {
91             ramblock_recv_bitmap_set_offset(p->block, p->zero[i]);
92         }
93     }
94 }
95