使用 blkdebug 注入块 I/O 错误
blkdebug 是基于规则的块驱动错误注入引擎,
可用于测试 ENOSPC(空间不足)、EIO 等错误处理路径。
本文概述其主要功能。
背景
块驱动有许多处理 I/O 错误的代码路径。 镜像格式尤其复杂:分配簇或更新表时发生的元数据 I/O 错误, 可能出现在请求处理的中途,需要谨慎处理才能保持文件一致。
错误注入让测试在指定位置触发 I/O 错误,从而验证各条错误路径。
规则
blkdebug 接受一组规则,决定何时让 I/O 请求失败。
每个请求都会与规则比较,匹配后执行相应动作。
规则可以保存在配置文件中,采用与 QEMU -readconfig 相同的类 INI 格式, 每个节定义一条规则。
以下配置定义一条规则::
$ cat blkdebug.conf
[inject-error]
event = "read_aio"
errno = "28"
它使所有异步读请求以 ENOSPC(28)失败。
errno 数值取决于宿主,Linux 的取值见 /usr/include/asm-generic/errno-base.h。
按以下方式运行 QEMU::
$ qemu-system-x86_64
-drive if=none,cache=none,file=blkdebug:blkdebug.conf:test.img,id=drive0 \
-device virtio-blk-pci,drive=drive0,id=virtio-blk-pci0
规则支持以下属性:
事件
块驱动会报告即将发出的 I/O 类型,便于规则精确匹配。 例如,qcow2 访问 L1 表时通知 blkdebug, 规则便能只匹配 L1 访问,而非其他元数据或客户机数据请求。
核心事件如下:
read_aio读取客户机数据。
write_aio写入客户机数据。
flush_to_os写出尚未持久化到操作系统的块驱动状态,例如缓存的元数据。
flush_to_disk刷新宿主块设备的磁盘缓存。
完整事件列表见 qapi/block-core.json:BlkdebugEvent。
有时需要搜索块驱动源码,理解特定事件的含义。
状态转换
有时需要从较长请求序列中匹配特定一次操作,例如::
write_aio
flush_to_disk
write_aio
若只匹配第二次 write_aio,就需要状态转换。
引擎内部维护整数 state,初始为 1。 外部无法直接观察它,但规则可以读写它,实现更强的匹配能力。
规则可以以当前状态为条件,并切换到新状态。
state 属性非零时,必须与当前状态相同才会匹配。
例如,匹配第二次 write_aio::
[set-state]
event = "write_aio"
state = "1"
new_state = "2"
[inject-error]
event = "write_aio"
state = "2"
errno = "5"
第一次请求匹配 set-state,将状态从 1 改为 2。 之后该规则不再匹配,因为它要求状态 1。 下一次 write_aio 则匹配 inject-error,注入 EIO(5)。
状态转换规则支持:
event要匹配的操作类型,例如 read_aio、write_aio、flush_to_os、flush_to_disk,详见 事件。
state可选。匹配所需的当前状态。
new_state要切换到的状态。
挂起和恢复
测试某些块驱动路径,需要让并发请求按特定顺序执行。 断点功能可以在 blkdebug 事件处暂停请求,稍后恢复, 从而在多个在途请求之间实现确定性顺序。
断点关联用户定义的 tag 字符串,用于稍后识别并恢复请求。
详见 qemu-io(1) 的 break、resume、remove_break 和 wait_break 命令。