dm-dust

此目标模拟任意位置的坏扇区行为,并能够在任意时间启用故障模拟。

此目标的行为类似于线性目标。在给定的时间,用户可以向目标发送消息,以开始在特定块上发生读取请求失败(模拟具有坏扇区的硬盘驱动器的行为)。

当启用故障行为时(即:“dmsetup status”的输出显示“fail_read_on_bad_block”时),读取“坏块列表”中的块将失败,并显示 EIO(“输入/输出错误”)。

写入“坏块列表”中的块将导致以下结果:

  1. 从“坏块列表”中删除该块。

  2. 成功完成写入。

这模拟了具有坏扇区的驱动器的“重新映射扇区”行为。

通常,遇到坏扇区的驱动器很可能会在未知的时间或位置遇到更多坏扇区。使用 dm-dust,用户可以使用 “addbadblock” 和 “removebadblock” 消息在新的位置添加任意坏块,并使用 “enable” 和 “disable” 消息来调整配置的 “坏块” 是否将被视为坏块或绕过的状态。这允许在模拟坏扇区开始出现的 “故障” 事件之前预先写入测试数据和元数据。

表参数

<device_path> <offset> <blksz>

强制参数
<device_path>

块设备的路径。

<offset>

从 device_path 的开头到数据区域的偏移量

<blksz>

块大小(以字节为单位)

(最小 512,最大 1073741824,必须是 2 的幂)

使用说明

首先,找到要使用的设备的大小(以 512 字节扇区为单位)

$ sudo blockdev --getsz /dev/vdb1
33552384

创建 dm-dust 设备:(对于块大小为 512 字节的设备)

$ sudo dmsetup create dust1 --table '0 33552384 dust /dev/vdb1 0 512'

(对于块大小为 4096 字节的设备)

$ sudo dmsetup create dust1 --table '0 33552384 dust /dev/vdb1 0 4096'

检查读取行为的状态(“bypass” 表示所有 I/O 将传递到基础设备;“verbose” 表示坏块添加、删除和重新映射将以详细方式记录)

$ sudo dmsetup status dust1
0 33552384 dust 252:17 bypass verbose

$ sudo dd if=/dev/mapper/dust1 of=/dev/null bs=512 count=128 iflag=direct
128+0 records in
128+0 records out

$ sudo dd if=/dev/zero of=/dev/mapper/dust1 bs=512 count=128 oflag=direct
128+0 records in
128+0 records out

添加和删除坏块

在任何时间(即:无论设备是否启用了“坏块”模拟),都可以通过 “addbadblock” 和 “removebadblock” 消息从设备添加或删除坏块

$ sudo dmsetup message dust1 0 addbadblock 60
kernel: device-mapper: dust: badblock added at block 60

$ sudo dmsetup message dust1 0 addbadblock 67
kernel: device-mapper: dust: badblock added at block 67

$ sudo dmsetup message dust1 0 addbadblock 72
kernel: device-mapper: dust: badblock added at block 72

这些坏块将存储在“坏块列表”中。当设备处于“bypass”模式时,读取和写入将成功

$ sudo dmsetup status dust1
0 33552384 dust 252:17 bypass

启用块读取失败

要启用 “坏块读取失败” 行为,请发送 “enable” 消息

$ sudo dmsetup message dust1 0 enable
kernel: device-mapper: dust: enabling read failures on bad sectors

$ sudo dmsetup status dust1
0 33552384 dust 252:17 fail_read_on_bad_block

当设备处于 “坏块读取失败” 模式时,尝试读取块将遇到 “输入/输出错误”

$ sudo dd if=/dev/mapper/dust1 of=/dev/null bs=512 count=1 skip=67 iflag=direct
dd: error reading '/dev/mapper/dust1': Input/output error
0+0 records in
0+0 records out
0 bytes copied, 0.00040651 s, 0.0 kB/s

...并且写入坏块将从列表中删除这些块,从而模拟硬盘驱动器的“重新映射”行为

$ sudo dd if=/dev/zero of=/dev/mapper/dust1 bs=512 count=128 oflag=direct
128+0 records in
128+0 records out

kernel: device-mapper: dust: block 60 removed from badblocklist by write
kernel: device-mapper: dust: block 67 removed from badblocklist by write
kernel: device-mapper: dust: block 72 removed from badblocklist by write
kernel: device-mapper: dust: block 87 removed from badblocklist by write

坏块添加/删除错误处理

尝试添加列表中已存在的坏块将导致 “无效参数” 错误以及有用的消息

$ sudo dmsetup message dust1 0 addbadblock 88
device-mapper: message ioctl on dust1  failed: Invalid argument
kernel: device-mapper: dust: block 88 already in badblocklist

尝试删除列表中不存在的坏块将导致 “无效参数” 错误以及有用的消息

$ sudo dmsetup message dust1 0 removebadblock 87
device-mapper: message ioctl on dust1  failed: Invalid argument
kernel: device-mapper: dust: block 87 not found in badblocklist

计算坏块列表中坏块的数量

要计算设备中配置的坏块数量,请运行以下消息命令

$ sudo dmsetup message dust1 0 countbadblocks

将打印一条消息,其中包含当前在设备上配置的坏块数量

countbadblocks: 895 badblock(s) found

查询特定坏块

要查找特定块是否在坏块列表中,请运行以下消息命令

$ sudo dmsetup message dust1 0 queryblock 72

如果该块在列表中,将打印以下消息

dust_query_block: block 72 found in badblocklist

如果该块不在列表中,将打印以下消息

dust_query_block: block 72 not found in badblocklist

“queryblock” 消息命令将在 “enabled” 和 “disabled” 模式下工作,允许验证块是否将被视为 “坏” 块,而无需向设备发出 I/O 或必须 “启用” 坏块模拟。

清除坏块列表

要清除坏块列表(而无需为每个块单独运行 “removebadblock” 消息命令),请运行以下消息命令

$ sudo dmsetup message dust1 0 clearbadblocks

清除坏块列表后,将出现以下消息

dust_clear_badblocks: badblocks cleared

如果没有要清除的坏块,将出现以下消息

dust_clear_badblocks: no badblocks found

列出坏块列表

要列出坏块列表中的所有坏块(使用坏块列表中包含块 1 和 2 的示例设备),请运行以下消息命令

$ sudo dmsetup message dust1 0 listbadblocks
1
2

如果坏块列表中没有坏块,则命令将执行,且没有输出

$ sudo dmsetup message dust1 0 listbadblocks

消息命令列表

以下是可以发送到 dust 设备的消息列表

对块的操作(需要 <blknum> 参数)

addbadblock <blknum>
queryblock <blknum>
removebadblock <blknum>

...其中 <blknum> 是设备范围内的块号(与设备的块大小相对应。)

单个参数消息命令

countbadblocks
clearbadblocks
listbadblocks
disable
enable
quiet

设备移除

完成后,通过 “dmsetup remove” 命令删除设备

$ sudo dmsetup remove dust1

静默模式

在包含大量坏块的测试运行中,可能需要避免过多的日志记录(来自添加、删除或“重新映射”的坏块)。可以通过发送以下消息启用“静默模式”来实现此目的。

$ sudo dmsetup message dust1 0 quiet

这将抑制来自添加/删除/通过写入操作移除的日志消息。“countbadblocks”或“queryblock”消息命令的日志消息在静默模式下仍会打印。

可以通过运行“dmsetup status”查看静默模式的状态。

$ sudo dmsetup status dust1
0 33552384 dust 252:17 fail_read_on_bad_block quiet

要禁用静默模式,请再次发送“quiet”消息。

$ sudo dmsetup message dust1 0 quiet

$ sudo dmsetup status dust1
0 33552384 dust 252:17 fail_read_on_bad_block verbose

(“verbose”的存在表示正常的日志记录。)

“为什么不...?”

scsi_debug 有一个“介质错误”模式,可以在一个指定的扇区(扇区 0x1234,在源代码中硬编码)上使读取失败,但它使用 RAM 作为持久存储,这会大大减少设备的潜在大小。

dm-flakey 会在指定的时间频率上使所有块位置的所有 I/O 失败,而不是在给定的时间点失败。

当硬盘驱动器上出现坏扇区时,设备会对该扇区的读取操作失败,通常会导致 EIO(“I/O 错误”)或 ENODATA(“无数据可用”)的错误代码。但是,对该扇区的写入操作可能会成功,并导致设备控制器不再遇到读取该扇区的错误后(或者在重新分配该扇区后)该扇区变为可读。然而,将来设备上可能会在不同的、不可预测的位置出现坏扇区。

此目标旨在提供一种设备,该设备可以在已知扇区位置,在已知时间,根据大型存储设备(至少数十千兆字节,不占用系统内存)来表现坏扇区的行为。