qemu/migration/multifd-zero-page.c
<<
>>
Prefs
   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 "system/ramblock.h"
  16#include "migration.h"
  17#include "migration-stats.h"
  18#include "multifd.h"
  19#include "options.h"
  20#include "ram.h"
  21
  22static bool multifd_zero_page_enabled(void)
  23{
  24    return migrate_zero_page_detection() == ZERO_PAGE_DETECTION_MULTIFD;
  25}
  26
  27static 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 */
  48void 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
  79out:
  80    stat64_add(&mig_stats.normal_pages, pages->normal_num);
  81    stat64_add(&mig_stats.zero_pages, pages->num - pages->normal_num);
  82}
  83
  84void 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        bool received =
  89                ramblock_recv_bitmap_test_byte_offset(p->block, p->zero[i]);
  90
  91        /*
  92         * During multifd migration zero page is written to the memory
  93         * only if it is migrated more than once.
  94         *
  95         * It becomes a problem when both multifd & postcopy options are
  96         * enabled. If the zero page which was skipped during multifd phase,
  97         * is accessed during the postcopy phase of the migration, a page
  98         * fault occurs. But this page fault is not served because the
  99         * 'receivedmap' says the zero page is already received. Thus the
 100         * thread accessing that page may hang.
 101         *
 102         * When postcopy is enabled, always write the zero page as and when
 103         * it is migrated.
 104         */
 105        if (migrate_postcopy_ram() || received) {
 106            memset(page, 0, multifd_ram_page_size());
 107        }
 108        if (!received) {
 109            ramblock_recv_bitmap_set_offset(p->block, p->zero[i]);
 110        }
 111    }
 112}
 113