Bitmap(位图)是一种常用的结构,通过使用 bit 位来记录一些逻辑状态。在 Qemu 中,Bitmap 用来记录 block 块设备的 sectors 是否为脏块,并被应用于多种常用的 block 操作中。
一、Qemu 中 Bitmap 结构简介
通常,一个 unsigned long 类型变量或数组即可作为 Bitmap 来记录状态,但当 Bitmap 比较大的时候,它的操作效率很低。Qemu 使用的 blockdrive 要使用 Bitmap 记录块设备的状态,每个块都对应一个 bit 位,按位表示会生成一个很大的 Bitmap,这会严重降低需要频繁查询 Bitmap 状态的块操作的执行效率。因此,Qemu 专门设计了 HBitmap 数据结构,并实现了 Bitmap 的查找、置位、计算总数、合并等操作。
HBitmap 数据结构如下:
struct HBitmap {
uint64_t size;
uint64_t count;
int granularity;
HBitmap meta;
unsigned long levels[HBITMAP_LEVELS];
uint64_t sizes[HBITMAP_LEVELS];
};
本文不过多涉及 Qemu HBitmap 内部的具体数据结构与寻址计算,细节可参考 Qemu 源码(util/hbitmap.c)中 HBitmap 相关操作接口的实现。
二、Qemu 中 Bitmap 的典型应用场景
Qemu 使用 Bitmap 记录磁盘状态,将 HBitmap 结构作为 Bitmap 的源数据封装在 BdrvDirtyBitmap 结构中,在 block 操作(e.g. drive-mirror,backup)中调用 bdrv 接口操作 Bitmap。
Drive-Mirror 过程中的 Bitmap 使用
Drive-mirror 操作可以动态的(vm 运行状态下)改变 block chain,主要应用于 live migrate 与 block-commit 。
Drive-mirror执行流程及主要调用函数如下图所示:
Drive-mirror 开始时,会先根据 block chain 生成 Bitmap,该 Bitmap 将用于后续的数据迭代拷贝过程。在 full 模式下,Bitmap 会根据完整 block chain 的状态生成;在 top 模式下,Bitmap 根据 active 层 block 状态生成。
Drive-mirror 过程中创建的 Bitmap 为匿名 Bitmap,drive-mirror操作结束后,会自行销毁,外部用户无法使用 qmp 命令对该 bitmap 进行删除,清空操作。
在 drive-mirror 过程中,Qemu 通过 bdrv_get_dirty_count 在各个操作间获取 Bitmap 记录的脏块个数。当脏块个数为零,且没有未决(正在处理的 IO 请求)数据时,Qemu 判断 mirror 到达 ready 状态并发送 ready 信号。当收到用户发送的 complete & cancel 消息时,Qemu 再次获取脏块数量,并在确认没有新增 IO 操作产生的数据时,进入 complete 过程结束 drive-mirror 过程。
在 drive-mirror 传输数据的迭代过程中,每次根据 Bitmap 标记的脏块状态,迁移一段连续的块数据,并清除这些块的 Bitmap 标记。当 vm 内部有对这些块的写操作时,相应的 Bitmap 位会被再次置位,因此 drive-mirror 过程需要持续迭代,直到 Bitmap 为空,即此时所有脏块已完成拷贝,且没有新的脏块产生,才达到 ready 状态。drive-mirror 结束后,该 Bitmap 会被自动销毁。
Backup Incremental 过程中的 Bitmap 使用
Drive-backup 操作可以拷贝当前的块设备状态到指定的目标设备,与 drive-mirror 的 block chain 的动态切换不同,backup 操作只生成当前镜像的副本,vm 仍然使用原有的镜像链。因此 backup 操作可以用于块设备的备份。
Backup 操作支持三种操作模式,full 模式拷贝完整 block chain 的数据;top 模式拷贝 active 层的数据。第三种 incremental 增量模式,则可以根据指定的 Bitmap 进行数据拷贝,即仅拷贝指定 Bitmap 标记的脏块到目的镜像,这使得执行 backup 操作时,可以在首次备份时使用 full 模式 backup,后续备份采取增量模式,以 full 模式备份的数据作为基准,从而无需每次都备份全量数据,加快了备份操作的拷贝速度。
典型的使用增量操作方式过程如下:
1. 执行full模式backup
2. 对block设备创建bitmap
3. 指定上一步创建的bitmap,执行增量操作
4. 执行第二次增量操作,以此类推
与 mirror 操作不同,用户在执行增量 backup 前,需要先手动创建命名 Bitmap,然后在增量任务开始时指定 Bitmap 名称。每次执行增量模式 backup 后,Qemu 会自动清空 Bitmap,确保每次 Bitmap 记录的都是两次增量操作间的 block 状态,从而确保增量数据没有重叠。
三、Bitmap 热迁移
Bitmap 记录块设备状态,被 Qemu 进程管理,并被应用于 Qemu 的块设备操作,因此当 vm 迁移时,Bitmap 也需要迁移。
Qemu 的热迁移包含源端与目的端两部分逻辑,数据以固定格式字节流的形式发送。Qemu 在迁移时,根据发送数据对象长度,循环发送字节流至目的端,热迁移源端主要流程如下:
RAM 的迁移是通过 iteration 分段传输的,但 Bitmap 迁移时,由于 vm 内部依然会有用户 IO 操作,而 Bitmap 需要记录写磁盘状态,因此如果分段迁移,就可能会破坏 Bitmap 在源端与目的端的一致性。
Qemu 进行 Bitmap 热迁移时,先判断如果 vCPU 处于 running 状态,则每次循环时不会分段传输 Bitmap 数据。Bitmap 会在 migration_completion 阶段,vCPU 被 stop,且 aio request 都被正确记录后,一次性传输至目的端。这使得当 vm 内部有 IO 操作时,Bitmap 仍然能正常记录 block 脏块,而不会出现部分 Bitmap 被迁走后,该段 Bitmap 对应的 block sector 又有数据写入,导致源端、目的端 Bitmap 数据不一致的情况。
四、Qemu Bitmap 相关 qmp 命令
Bitmap 与 block 设备相关联,可以通过 qmp 命令 { “execute”: “query-block”} 查看 block,同时获取所有与块设备相关联的命名与匿名 Bitmap 状态。此外,Qemu monitor 提供了 Bitmap 的基本操作命令,如下:
向指定块设备添加 Bitmap
{ "execute": "block-dirty-bitmap-add",
"arguments": { "node": "drive0", "name": "bitmap0" } }
删除指定块设备上的 Bitmap
{ "execute": "block-dirty-bitmap-remove",
"arguments": { "node": "drive0", "name": "bitmap0" } }
清空 Bitmap 状态
{ "execute": "block-dirty-bitmap-clear",
"arguments": { "node": "drive0", "name": "bitmap0" } }
注意 qmp 命令只能对命名 Bitmap 操作,无法对像 drive-mirror 过程中由任务创建的匿名 Bitmap 进行操作。
本文作者:马昊骢
1044



被折叠的 条评论
为什么被折叠?



