1*303e6f54SHao Xiang /*
2*303e6f54SHao Xiang  * Multifd zero page detection implementation.
3*303e6f54SHao Xiang  *
4*303e6f54SHao Xiang  * Copyright (c) 2024 Bytedance Inc
5*303e6f54SHao Xiang  *
6*303e6f54SHao Xiang  * Authors:
7*303e6f54SHao Xiang  *  Hao Xiang <hao.xiang@bytedance.com>
8*303e6f54SHao Xiang  *
9*303e6f54SHao Xiang  * This work is licensed under the terms of the GNU GPL, version 2 or later.
10*303e6f54SHao Xiang  * See the COPYING file in the top-level directory.
11*303e6f54SHao Xiang  */
12*303e6f54SHao Xiang 
13*303e6f54SHao Xiang #include "qemu/osdep.h"
14*303e6f54SHao Xiang #include "qemu/cutils.h"
15*303e6f54SHao Xiang #include "exec/ramblock.h"
16*303e6f54SHao Xiang #include "migration.h"
17*303e6f54SHao Xiang #include "multifd.h"
18*303e6f54SHao Xiang #include "options.h"
19*303e6f54SHao Xiang #include "ram.h"
20*303e6f54SHao Xiang 
21*303e6f54SHao Xiang static bool multifd_zero_page_enabled(void)
22*303e6f54SHao Xiang {
23*303e6f54SHao Xiang     return migrate_zero_page_detection() == ZERO_PAGE_DETECTION_MULTIFD;
24*303e6f54SHao Xiang }
25*303e6f54SHao Xiang 
26*303e6f54SHao Xiang static void swap_page_offset(ram_addr_t *pages_offset, int a, int b)
27*303e6f54SHao Xiang {
28*303e6f54SHao Xiang     ram_addr_t temp;
29*303e6f54SHao Xiang 
30*303e6f54SHao Xiang     if (a == b) {
31*303e6f54SHao Xiang         return;
32*303e6f54SHao Xiang     }
33*303e6f54SHao Xiang 
34*303e6f54SHao Xiang     temp = pages_offset[a];
35*303e6f54SHao Xiang     pages_offset[a] = pages_offset[b];
36*303e6f54SHao Xiang     pages_offset[b] = temp;
37*303e6f54SHao Xiang }
38*303e6f54SHao Xiang 
39*303e6f54SHao Xiang /**
40*303e6f54SHao Xiang  * multifd_send_zero_page_detect: Perform zero page detection on all pages.
41*303e6f54SHao Xiang  *
42*303e6f54SHao Xiang  * Sorts normal pages before zero pages in p->pages->offset and updates
43*303e6f54SHao Xiang  * p->pages->normal_num.
44*303e6f54SHao Xiang  *
45*303e6f54SHao Xiang  * @param p A pointer to the send params.
46*303e6f54SHao Xiang  */
47*303e6f54SHao Xiang void multifd_send_zero_page_detect(MultiFDSendParams *p)
48*303e6f54SHao Xiang {
49*303e6f54SHao Xiang     MultiFDPages_t *pages = p->pages;
50*303e6f54SHao Xiang     RAMBlock *rb = pages->block;
51*303e6f54SHao Xiang     int i = 0;
52*303e6f54SHao Xiang     int j = pages->num - 1;
53*303e6f54SHao Xiang 
54*303e6f54SHao Xiang     if (!multifd_zero_page_enabled()) {
55*303e6f54SHao Xiang         pages->normal_num = pages->num;
56*303e6f54SHao Xiang         return;
57*303e6f54SHao Xiang     }
58*303e6f54SHao Xiang 
59*303e6f54SHao Xiang     /*
60*303e6f54SHao Xiang      * Sort the page offset array by moving all normal pages to
61*303e6f54SHao Xiang      * the left and all zero pages to the right of the array.
62*303e6f54SHao Xiang      */
63*303e6f54SHao Xiang     while (i <= j) {
64*303e6f54SHao Xiang         uint64_t offset = pages->offset[i];
65*303e6f54SHao Xiang 
66*303e6f54SHao Xiang         if (!buffer_is_zero(rb->host + offset, p->page_size)) {
67*303e6f54SHao Xiang             i++;
68*303e6f54SHao Xiang             continue;
69*303e6f54SHao Xiang         }
70*303e6f54SHao Xiang 
71*303e6f54SHao Xiang         swap_page_offset(pages->offset, i, j);
72*303e6f54SHao Xiang         ram_release_page(rb->idstr, offset);
73*303e6f54SHao Xiang         j--;
74*303e6f54SHao Xiang     }
75*303e6f54SHao Xiang 
76*303e6f54SHao Xiang     pages->normal_num = i;
77*303e6f54SHao Xiang }
78*303e6f54SHao Xiang 
79*303e6f54SHao Xiang void multifd_recv_zero_page_process(MultiFDRecvParams *p)
80*303e6f54SHao Xiang {
81*303e6f54SHao Xiang     for (int i = 0; i < p->zero_num; i++) {
82*303e6f54SHao Xiang         void *page = p->host + p->zero[i];
83*303e6f54SHao Xiang         if (!buffer_is_zero(page, p->page_size)) {
84*303e6f54SHao Xiang             memset(page, 0, p->page_size);
85*303e6f54SHao Xiang         }
86*303e6f54SHao Xiang     }
87*303e6f54SHao Xiang }
88