951 字
5 分钟
ram_load_precopy处理 RAM 数据接收的核心函数
2025-09-01
无标签

这段代码是 QEMU 热迁移中目标端处理 RAM 数据接收的核心函数。让我为您详细解释:

🎯 函数概述#

ram_load_precopy() - 在预拷贝模式下加载内存页数据

作用: 目标端从迁移流中读取并恢复源端发送的内存页数据


🔧 初始化和配置检查#

MigrationIncomingState *mis = migration_incoming_get_current();
int flags = 0, ret = 0, invalid_flags = 0, len = 0, i = 0;

// 根据配置设置无效标志
if (!migrate_compress()) {
    invalid_flags |= RAM_SAVE_FLAG_COMPRESS_PAGE;  // 不支持压缩页
}

if (migrate_mapped_ram()) {
    // mapped-ram模式下,某些标志无效
    invalid_flags |= (RAM_SAVE_FLAG_HOOK | RAM_SAVE_FLAG_MULTIFD_FLUSH |
                      RAM_SAVE_FLAG_PAGE | RAM_SAVE_FLAG_XBZRLE |
                      RAM_SAVE_FLAG_ZERO);
}

目的: 根据迁移配置确定哪些数据格式是不被支持的


🔄 主处理循环#

while (!ret && !(flags & RAM_SAVE_FLAG_EOS)) {  // 直到遇到结束标志
    ram_addr_t addr;
    void *host = NULL, *host_bak = NULL;
    uint8_t ch;
    
    // 🎯 关键优化:周期性让出控制权
    if ((i & 32767) == 0 && qemu_in_coroutine()) {
        aio_co_schedule(qemu_get_current_aio_context(),
                        qemu_coroutine_self());
        qemu_coroutine_yield();  // 让出CPU给其他任务	
    }
    i++;

重要优化: 每处理 32768 个页面后让出控制权,避免长时间占用 CPU 影响 Guest OS 运行


📦 数据解析和验证#

addr = qemu_get_be64(f);        // 读取64位地址+标志
flags = addr & ~TARGET_PAGE_MASK;  // 提取标志位
addr &= TARGET_PAGE_MASK;          // 提取实际地址

if (flags & invalid_flags) {
    error_report("Unexpected RAM flags: %d", flags & invalid_flags);
    ret = -EINVAL;
    break;
}

数据格式: 地址和标志打包在一个 64 位值中,高位是标志,低位是页面地址


🏠 内存块定位和 COLO 优化#

if (flags & (RAM_SAVE_FLAG_ZERO | RAM_SAVE_FLAG_PAGE | ...)) {
    RAMBlock *block = ram_block_from_stream(mis, f, flags, RAM_CHANNEL_PRECOPY);
    host = host_from_ram_block_offset(block, addr);  // 获取目标内存地址
    
    // 🎯 COLO优化:容灾备份优化
    if (migration_incoming_colo_enabled()) {
        if (migration_incoming_in_colo_state()) {
            // COLO阶段:所有页面暂存到缓存
            host = colo_cache_from_block_offset(block, addr, true);
        } else {
            // 迁移阶段:同时写入缓存和内存
            host_bak = colo_cache_from_block_offset(block, addr, false);
        }
    }
}

COLO 优化:

  • 迁移阶段: 页面同时写入内存和 COLO 缓存,减少后续备份时间
  • COLO 阶段: 页面只写入缓存,不直接修改 SVM 内存

🔀 不同数据类型处理#

switch (flags & ~RAM_SAVE_FLAG_CONTINUE) {
    case RAM_SAVE_FLAG_MEM_SIZE:
        ret = parse_ramblocks(f, addr);  // 解析内存块信息
        if (migrate_mapped_ram()) {
            multifd_recv_sync_main();    // 多线程同步
        }
        break;
        
    case RAM_SAVE_FLAG_ZERO:
        ch = qemu_get_byte(f);
        ram_handle_zero(host, TARGET_PAGE_SIZE);  // 零页处理
        break;
        
    case RAM_SAVE_FLAG_PAGE:
        qemu_get_buffer(f, host, TARGET_PAGE_SIZE);  // 普通页面
        break;
        
    case RAM_SAVE_FLAG_COMPRESS_PAGE:
        len = qemu_get_be32(f);
        decompress_data_with_multi_threads(f, host, len);  // 压缩页面
        break;
        
    case RAM_SAVE_FLAG_XBZRLE:
        if (load_xbzrle(f, addr, host) < 0) {  // 增量压缩
            error_report("Failed to decompress XBZRLE page");
            ret = -EINVAL;
        }
        break;
        
    case RAM_SAVE_FLAG_MULTIFD_FLUSH:
        multifd_recv_sync_main();  // 多线程刷新同步
        break;
        
    case RAM_SAVE_FLAG_EOS:
        // 正常结束,可能需要多线程同步
        if (migrate_multifd() && !migrate_mapped_ram()) {
            multifd_recv_sync_main();
        }
        break;
}

各种页面类型:#

  • MEM_SIZE: 内存布局信息
  • ZERO: 全零页面 (优化传输)
  • PAGE: 普通页面数据
  • COMPRESS_PAGE: 压缩页面
  • XBZRLE: 增量压缩页面
  • MULTIFD_FLUSH: 多线程同步点
  • EOS: 数据流结束

🎯 关键设计特点#

1️⃣ 性能优化#

// 周期性让出控制权,避免影响Guest OS
if ((i & 32767) == 0 && qemu_in_coroutine()) {
    qemu_coroutine_yield();
}

2️⃣ 多种数据格式支持#

  • 支持压缩、增量压缩、零页优化等多种传输优化

3️⃣ COLO 容灾优化#

// 在迁移过程中同时备份,减少后续COLO准备时间
if (migration_incoming_colo_enabled()) {
    host_bak = colo_cache_from_block_offset(block, addr, false);
}

4️⃣ 多线程协调#

  • 支持多线程接收,在关键点进行同步

💡 总结#

这个函数是 QEMU 热迁移目标端的核心,负责:

  1. 🔄 持续接收源端发送的内存页数据
  2. 🎯 智能处理不同类型的页面 (零页、压缩页、增量页等)
  3. ⚡ 性能优化周期性让出 CPU,不影响系统响应
  4. 🛡️ 容灾支持 COLO 模式下的优化备份策略
  5. 🔗 多线程协调支持并行传输和同步

这就是为什么热迁移能够高效传输大量内存数据的关键实现!

ram_load_precopy处理 RAM 数据接收的核心函数
https://scudays.github.io/posts/icsas/ram迁移/ram_load_precopy处理-ram-数据接收的核心函数/
作者
Days
发布于
2025-09-01
许可协议
CC BY-NC-SA 4.0