首页
/ Linux dm-flakey 使用详解:基于 Device Mapper 周期性模拟设备故障与数据损坏的故障注入 target

Linux dm-flakey 使用详解:基于 Device Mapper 周期性模拟设备故障与数据损坏的故障注入 target

2026-09-07 13:22:04作者:凌朦慧Richard

dm-flakey 是 Linux 内核 Device Mapper 子系统中的一个专用 target,它建立在 linear 映射之上,但会按固定周期在"正常可用"与"不可靠故障"两种状态之间切换,从而以低成本、高可控的方式模拟磁盘随机掉盘、间歇性坏盘等场景,是文件系统与块层代码故障路径测试的常用工具。本文以 dm-flakey.rst 为主线,结合其内核实现 dm-flakey.c,完整讲解表语法、全部 feature 参数、数据破坏(corruption)的底层执行机制,并给出可直接复制运行的 dmsetup 实战示例,帮助你快速搭建属于自己的"故障注入测试盘"。

dm-flakey 定位:一个能周期"犯病"的线性映射

Device Mapper(DM)允许通过用户态工具(如 dmsetup)把若干块设备按某种 target 规则重映射为一个新的逻辑设备。dm-flakey 属于块层 DM 框架下的一个 target 类型,官方文档开篇即给出其定位:

该 target 与 linear target 相同,区别在于它会周期性表现出不可靠行为,在模拟故障设备以进行测试方面很有用。

从内核实现看,其目标类型声明位于 dm-flakey.c,名称为 "flakey",版本号 {1, 5, 0},并声明了 DM_TARGET_ZONED_HM | DM_TARGET_PASSES_CRYPTO 特性。头文件注释同样强调"仅供测试,模拟间歇性、灾难性设备故障"(Flakey: Used for testing only, simulates intermittent, catastrophic device failure.)。

该功能默认不启用,需在内核配置中打开 CONFIG_DM_FLAKEYdrivers/md/Kconfig 中描述为 "A target that intermittently fails I/O for debugging purposes."),其编译产物为独立的 dm-flakey.o 模块(见 drivers/md/Makefile)。配置开启后可选择静态编译或按需加载模块,测试环境常用 modprobe dm-flakey

失效模型:up/down 间隔构成的"可用—故障"循环

dm-flakey 的失效模型非常朴素:从表加载那一刻开始计时,设备先可用 <up interval> 秒,随后进入 <down interval> 秒的不可靠期,如此循环往复

对应到源码 flakey_map,每次 I/O 到达时都会根据"表加载时记录的时间戳 fc->start_time(jiffies)"计算已流逝的秒数并判定当前所处阶段:

elapsed = (jiffies - fc->start_time) / HZ;
if (elapsed % (fc->up_interval + fc->down_interval) >= fc->up_interval) {
    /* 进入 down 期:按 feature 配置返回错误、静默丢弃或破坏数据 */
}

也就是说,每个完整周期长度为 up + down 秒,其中前 up 秒正常透传、后 down 秒施加故障。文档中还特别建议将 dm-flakey 与 dm-delay target 组合使用——后者可以延迟读写、或将读写定向到不同的底层设备(见 delay.rst),从而在同一套测试床中叠加"延迟 + 故障"两种异常。

表结构与参数语法

dm-flakey 的表(table)完整语法为:

<sector_start> <sector_len> flakey <dev path> <offset> <up interval> <down interval> \
    [<num_features> [<feature arguments>]]

其中前两列 <sector_start> <sector_len> 是 Device Mapper 所有 target 共有的"逻辑起始扇区与长度",文档中展开的 target 专属参数依次如下:

参数 含义
<dev path> 底层块设备的完整路径(如 /dev/sdb),或 major:minor 设备号
<offset> 映射在底层设备上的起始扇区
<up interval> 设备正常可用(透传 I/O)的秒数
<down interval> 设备进入故障状态、开始返回错误的秒数
[<num_features> [...]] 可选特性参数个数及具体参数(见下节)

源码侧对强制参数做了严格校验(见 flakey_ctr):

  • 参数少于 4 个直接报 "Invalid argument count";
  • <offset> 必须是无符号整数扇区号;
  • updown 各自的取值范围为 0 ~ UINT_MAX,二者之和既不能为 0("Total (up + down) interval is zero"),也不能发生回绕溢出("Interval overflow")。

特性参数个数如何数?

<num_features> 计的是该特性组后续所有参数令牌的总数。这一点可以从 flakey_status 生成 dmsetup table 输出时的计数规则得到印证:corrupt_bio_byte 记为 5(关键字自身 + 4 个参数),random_read_corrupt / random_write_corrupt 各记为 2(关键字 + 1 个参数),error_readsdrop_writeserror_writes 各记为 1。解析侧通过 dm_arg_group 限定最多 11 个特性参数(见 parse_features)。

可选特性参数:五种"坏法"全解析

不提供任何 feature 参数时,down 期间所有读、写 I/O 一律返回错误——对应源码中的默认回退逻辑(dm-flakey.c),即同时置位 ERROR_WRITESERROR_READS

error_reads:读全部报错,写正常

down 期间所有读 I/O 都被以错误信号终止,而写 I/O 正常处理。源码中当读方向命中该 flag 时直接返回 DM_MAPIO_KILLdm-flakey.c),相当于在映射层"掐死"这次读请求。文档提示它常用来模拟"读到一半设备就绪状态不稳"的场景。

drop_writes:写被静默吞掉,读正常

down 期间所有写 I/O 被静默忽略:既不下发到底层设备,也不报错,上层看到的是"写成功了",但数据实际丢失——这正是模拟"写缓存丢失、掉电丢数据"类故障的利器。源码实现为直接调用 bio_endio(bio) 并返回 DM_MAPIO_SUBMITTEDdm-flakey.c),即伪造一次成功结束。

error_writes:写全部报错,读正常

drop_writes 相反,down 期间写 I/O 显式返回错误,便于测试上层对"写失败"的报错与重试路径。源码通过 bio_io_error(bio) 结束请求(dm-flakey.c)。

三个基础 flag 之间存在互斥校验:drop_writeserror_writes 不能同时出现(重复出现或相互冲突都会在 parse_features 被拒绝)。

corrupt_bio_byte:定向篡改 bio 中的第 N 个字节

这是 dm-flakey 最具"破坏力"的特性,语法为:

corrupt_bio_byte <Nth_byte> <direction> <value> <flags>
  • <Nth_byte>:要替换的字节偏移,从 1 开始计数(1 即替换第一个字节)。源码内部会先减 1 再作为数组下标使用(corrupt_bio_data),取值范围校验为 1 ~ UINT_MAX
  • <direction>r 表示破坏读数据,w 表示破坏写数据,大小写均可(解析代码用 strcasecmp 判断,dm-flakey.c);方向为 w 时与 drop_writes 不兼容;
  • <value>:要写入的字节值,合法区间 0-255
  • <flags>:仅当 bio->bi_opf 包含全部所选标志时才执行替换,选中的过滤逻辑见宏 all_corrupt_bio_flags_matchdm-flakey.c):(bio->bi_opf & flags) == flags。当 flags = 0 时不加过滤,命中所有该方向的 bio。

文档给出的两个可直接照抄的示例:

# 将 READ 类型 bio 数据中的第 32 个字节替换为值 1
corrupt_bio_byte 32 r 1 0

# 将携带 REQ_META(=32) 标志的写 bio 中的第 224 个字节替换为 0
corrupt_bio_byte 224 w 0 32

第二个例子演示了"精准定向投毒":REQ_META 标志值恰好为 32,配合方向 w,可将范围精确到文件系统元数据写路径,非常适合模拟"元数据被损坏"的介质故障。方向 r/werror_reads/error_writes/drop_writes 之间还有一组成体系的互斥校验(见 dm-flakey.c),例如"drop_writes 不能与随机/定向的写破坏共存"。

random_read_corrupt / random_write_corrupt:按概率随机破坏

语法分别如下:

random_read_corrupt <probability>
random_write_corrupt <probability>

down 期间按 <probability> 概率在随机位置写入一个随机字节来破坏数据。probability0 ~ 1000000000 的整数,语义是 0% ~ 100% 的破坏概率(因为 PROBABILITY_BASE = 1000000000,见 dm-flakey.c)。源码实现上取一个 64 位随机数并对 PROBABILITY_BASE 取模,模值小于概率参数即触发破坏(dm-flakey.cflakey_end_io)。

各特性关键字都不允许重复出现;无法识别的关键字会得到 "Unrecognised flakey feature requested" 的报错。

源码视角:写破坏为什么要克隆 bio,读破坏又为何放在 end_io

深入 flakey_mapflakey_end_io 可以看到,读、写两类"破坏"的执行路径完全不同:

  • 写方向的破坏:down 期间命中 corrupt_bio_byte(w) 或 random_write_corrupt 时,内核不会直接修改原 bio 的页——因为写 bio 的页可能正承载着上层用户的真实数据。它先调用 clone_bio 把数据整体拷贝到新分配的内存页上构成克隆 bio,只对克隆体实施字节篡改,再提交克隆体完成真正的写盘(dm-flakey.c)。克隆完成后通过 clone_endiobi_status 回传给原始 bio。若内存分配失败,则该次写退化为正常透传(不破坏、不报错)。
  • 读方向的破坏:读数据来自底层设备,map 阶段拿不到内容,因此读破坏被延后到 I/O 完成之后flakey_end_io 回调中执行:只有当读成功结束(!*error)且该 bio 在 down 期被标记为可破坏(pb->bio_can_corrupt)时,才按保存好的原始迭代器 pb->saved_iter 定位并替换对应字节(dm-flakey.c)。这也解释了为何 error_reads 与读破坏互斥——报错路径上没有可返回给上层的数据可供篡改。

字节定位与写入的公共实现是 corrupt_bio_common:它遍历 bio 的各个 segment,定位到目标字节所在的页,通过 kmap_local 写入后落盘,并打印一条包含 rwbi_opf、扇区与尺寸的 DMDEBUG 日志(Corrupting data bio=...),供调试时用 dmsetup 配合动态调试开关观察。

实战:构造一条完整可用的 flakey 映射

环境准备

  • 内核已启用 CONFIG_DM_FLAKEY(可静态编译或 modprobe dm-flakey);
  • 用户态具备 dmsetup(device-mapper 工具);
  • 准备一块测试用底层设备,例如 /dev/sdb切勿使用承载重要数据的设备)。

下面的示例沿用文档相邻 target 文档(如 linear.rst)中 blockdev --getsz 的惯用法,自动把整个设备映射为一块 flakey 盘。

示例一:每 10 秒"健康"、2 秒"全 I/O 报错",不指定任何 feature(默认读、写全部返回错误):

echo "0 $(blockdev --getsz /dev/sdb) flakey /dev/sdb 0 10 2" | dmsetup create flaky-ioerr

示例二:挂载点写入"静默丢失"(30 秒正常 + 5 秒丢弃一切写,期间读正常返回)——可用于验证掉电/写缓存丢失场景下的上层行为:

echo "0 $(blockdev --getsz /dev/sdb) flakey /dev/sdb 0 30 5 1 drop_writes" | dmsetup create flaky-drop

示例三:down 期把每次读请求的第 32 个字节篡改为 1(对应文档首个示例;5 表示后续共有 5 个令牌):

echo "0 $(blockdev --getsz /dev/sdb) flakey /dev/sdb 0 10 5 5 corrupt_bio_byte 32 r 1 0" \
    | dmsetup create flaky-corrupt

示例四:对写路径的元数据精确投毒(写 bio 第 224 字节且仅当 bio 带 REQ_META(=32) 标志时改写为 0,对应文档第二个示例):

echo "0 $(blockdev --getsz /dev/sdb) flakey /dev/sdb 0 10 5 5 corrupt_bio_byte 224 w 0 32" \
    | dmsetup create flaky-meta

上述设备创建后可正常执行 mkfsmount、运行目标测试程序或故障演练脚本;在 down 窗口内即可观察到预期的 I/O 错误、数据丢失或字节损坏。常用的核查与清理命令:

dmsetup table flaky-corrupt   # 回显当前表,可用于核对 feature 参数是否完整
dmsetup status flaky-corrupt  # 查看状态信息
dmsetup remove flaky-corrupt  # 卸载该映射

其中 dmsetup table 的输出直接来自 flakey_statusSTATUSTYPE_TABLE 分支,会按上文计数规则重新拼出带 num_features 的完整表行,非常适合用来确认手工拼写的参数个数是否正确。

边界行为与使用注意事项

从源码可以确认以下边界细节,使用时应心中有数:

  • Zone 管理类操作透传:对 zoned 设备的 zone mgmt 类 bio,在 flakey_map 中直接走正常映射,不参与故障注入;在启用 CONFIG_BLK_DEV_ZONED 时还实现了 flakey_report_zones 以支持 host-managed zoned 设备场景;
  • flush/discard 支持num_flush_biosnum_discard_bios 均置为 1,即每个 flush/discard 请求会被透传处理,故障期对其施加的仍是上述错误/丢弃/破坏策略;
  • ioctl 透传受限flakey_prepare_ioctl 规定只有 offset == 0 且映射长度与底层设备完全一致时,才把 ioctl(如设备参数查询)转发给底层设备,否则拒绝转发,避免因映射错位产生误导性的设备信息;
  • 状态回显dmsetup statusSTATUSTYPE_INFO)默认返回空串,完整表信息应通过 dmsetup table 查看;
  • 仅用于测试:该 target 从 Kconfig 到代码注释都明确其为调试用途("for debugging purposes"),不应作为生产存储方案的一部分;它与相邻的 dm-dust、dm-delay 等一样属于 Device Mapper 的"故障与异常注入"工具族,dm-init.rst 甚至将 flakey 明确标注为 "constrained, meant for test",即也支持在内核引导早期通过 dm-mod.create= 参数加载,但用途限定为测试。

组合思路:把 flakey 放进更真实的故障场景

单靠 flakey 只能模拟"周期性整机故障",而真实系统故障往往叠加延迟、部分设备失效等因素。文档明确建议与 dm-delay 联合使用:dm-delay 可分别针对读、写、flush 设置毫秒级延迟,甚至把写操作重定向到另一块设备(delay.rst 提供了完整的 3/6/9 参数三套示例脚本)。一个典型的叠加玩法是先用 dm-delay 构造"写慢盘",再把 flakey 或 flakey+drop_writes 套在其上层或并行映射,从而在测试文件系统时同时覆盖"慢写 + 静默丢写 + 周期性全故障"的复合故障面;配合 corrupt_bio_byte 224 w 0 32 这类元数据定向破坏,还能精准验证日志型文件系统对元数据校验失败的恢复路径。

相关仓库文件速查

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.13 K
2.75 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
897
5.8 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
529
593
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
916
1.83 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.58 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.35 K
1.46 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.01 K
515
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
547
388