KVM同步脏页位图到Qemu

本文详细介绍了Qemu2.6.0和Linux3.10.0内核环境下,KVM如何将脏页位图同步到Qemu的过程。Qemu通过address_space_sync_dirty_bitmap和kvm_get_dirty_pages_log_range等函数处理内核返回的位图,而内核通过kvm_vm_ioctl(KVM_GET_DIRTY_LOG)接口传递脏页信息并清除脏标志。整个过程涉及热迁移、EPT页表和不同对齐情况下的位图处理。
摘要由CSDN通过智能技术生成

本篇文章基于qemu2.6.0,Linux3.10.0内核,x86体系结构。Qemu与KVM同步脏页位图主要分为Qemu与KVM两部分,我们先来看看Qemu部分。

Qemu部分

主要内容:Qemu将KVM返回的位图设置到自己的ram_list->dirty_memory->blocks中的map位图

address_space_sync_dirty_bitmap

主要调用了kvm_log_sync将kvm内核中的位图(kvm_memory_slot.dirty_bitmap)同步到qemu。本函数在热迁移过程中被migration_bitmap_sync函数调用。

void address_space_sync_dirty_bitmap(AddressSpace *as)
{
   
    FlatView *view;
    FlatRange *fr;

    view = address_space_get_flatview(as);
    /* 对每一个flat range调用memory_listeners里的所有log_sync。由于只有kvm_memory_listener中
     * log_sync是初始化了的,所以相当于对每一个flat range调用kvm_memory_listener中log_sync
     */
    FOR_EACH_FLAT_RANGE(fr, view) {
   
        //这里调用kvm_log_sync
        MEMORY_LISTENER_UPDATE_REGION(fr, as, Forward, log_sync);
    }
    
    flatview_unref(view);
}

kvm_log_sync

//这是下面kvm_log_sync函数的第二个参数section,一个FlatRange转换成一个MemoryRegionSection
(&(MemoryRegionSection) {
          \
        .mr = (fr)->mr,                                                 \
        .address_space = (as),                                          \
        .offset_within_region = (fr)->offset_in_region,                 \
        .size = (fr)->addr.size,                                       \
        .offset_within_address_space = int128_get64((fr)->addr.start),  \//起始物理地址
        .readonly = (fr)->readonly,                                     \
              })

static void kvm_log_sync(MemoryListener *listener,
                         MemoryRegionSection *section)
{
   
    //这里返回listener所属的KVMMemoryListener。每个MemoryListener都包含在一个KVMMemoryListener
    //大家或许有疑问:这里返回KVMMemoryListener干嘛?KVMMemoryListener串起了整个VM物理地址空间对应的
    //KVMSlot。所以下面kvm_physical_sync_dirty_bitmap的section就可以通过kml找到对应的KVMSlot
    KVMMemoryListener *kml = container_of(listener, KVMMemoryListener, listener);
    int r;

    r = kvm_physical_sync_dirty_bitmap(kml, section);
    if (r < 0) {
   
        abort();
    }
}

static int kvm_physical_sync_dirty_bitmap(KVMMemoryListener *kml,
                                          MemoryRegionSection *section)
{
   
    KVMState *s = kvm_state;
    unsigned long size, allocated_size = 0;
    struct kvm_dirty_log d = {
   };
    KVMSlot *mem;
    int ret = 0;
    //这里计算出section对应的物理地址区间
    hwaddr start_addr = section->offset_within_address_space;
    hwaddr end_addr = start_addr + int128_get64(section->size);

    d.dirty_bitmap = NULL;
    while (start_addr < end_addr) {
   
        //根据物理地址区间找到对应的KVMSlot
        mem = kvm_lookup_overlapping_slot(kml, start_addr, end_addr);
        ......
        //把物理地址区间大小转换成 页框数对应位图的字节数。一个页框对应位图的一个位
        //所以这里除8就是要分配位图的字节数
        size = ALIGN(((mem->memory_size) >> TARGET_PAGE_BITS),
                     /*HOST_LONG_BITS*/ 64) / 8;
        if (!d.dirty_bitmap) {
   
            d.dirty_bitmap = g_malloc(size);//先给位图分配空间
        } else if (size > allocated_size) {
   
            d.dirty_bitmap = g_realloc(d.dirty_bitmap, size);
        }
        allocated_size = size;
        //设置为全0,d.dirty_bitmap就是等下要传递给内核的参数,让内核将其脏页位图同步到d.dirty_bitmap
        memset(d.dirty_bitmap, 0, allocated_size);

        //把地址空间id 和 KVMSlot编号传递给内核,以便其查找到对应的 kvm_memory_slot 
        d.slot = mem->slot | (kml->as_id << 16);
        
        //主要完成将内核slot的脏页位图设置到d.dirty_bitmap。且重置内核slot的脏页位图
        //和清除EPT页表项的脏标志。KVM使用 kvm_vm_ioctl_get_dirty_log 完成以上操作
        //具体过程请看 内核部分
        if (kvm_vm_ioctl(s, KVM_GET_DIRTY_LOG, &d) == -1) {
   
            DPRINTF("ioctl failed %d\n", errno);
            ret = 
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值