dm-log-writes

此目标使用 2 个设备,一个用于正常传递所有 IO,另一个用于记录所有写入操作。这适用于希望验证文件系统写入时元数据或数据完整性的文件系统开发人员。每个 WRITE 请求都会写入一个 log_write_entry,并且目标可以从用户空间获取任意数据以插入到日志中。WRITE 请求中的数据会被复制到日志中,以使重放完全按照原始发生的方式进行。

日志排序

一旦我们确定写入不再位于缓存中,我们就会按照完成的顺序记录内容。这意味着正常的 WRITE 请求实际上要等到下一个 REQ_PREFLUSH 请求才会记录。这是为了使用户空间更容易以与磁盘上的内容而不是缓存中的内容相关联的方式重放日志,从而更容易检测不正确的等待/刷新。

其工作原理是在写入完成后将所有 WRITE 请求附加到列表中。一旦我们看到 REQ_PREFLUSH 请求,我们就会将此列表拼接在请求上,并且一旦 FLUSH 请求完成,我们就会记录所有 WRITE,然后记录 FLUSH。只有在发出 REQ_PREFLUSH 时已完成的 WRITE 会按顺序添加,以模拟关于电源故障的最坏情况。考虑以下示例(W 表示写入,C 表示完成)

W1,W2,W3,C3,C2,Wflush,C1,Cflush

日志将显示以下内容

W3,W2,flush,W1....

同样,这是为了模拟磁盘上的实际内容,这使我们能够检测在特定时间点发生电源故障会导致文件系统不一致的情况。

任何 REQ_FUA 请求都会绕过此刷新机制,并在完成时立即记录,因为这些请求显然会绕过设备缓存。

任何 REQ_OP_DISCARD 请求都被视为 WRITE 请求。否则,我们将拥有所有的 DISCARD 请求,然后是 WRITE 请求,然后是 FLUSH 请求。考虑以下示例

写入块 1,丢弃块 1,刷新

如果我们完成时记录 DISCARD,则重放将如下所示

丢弃 1,写入 1,刷新

这与实际发生的情况不太一样,也不会在日志重放期间被捕获。

目标接口

  1. 构造函数

    log-writes <dev_path> <log_dev_path>

    dev_path

    所有 IO 将正常转到的设备。

    log_dev_path

    写入日志条目的设备。

  2. 状态

    <#记录的条目> <最高分配扇区>

    #记录的条目

    记录的条目数

    最高分配扇区

    最高分配扇区

  3. 消息

标记 <描述>

您可以使用 dmsetup 消息在日志中设置任意标记。例如,假设您希望在每次写入后对文件系统进行 fsck,但首先您需要重放到 mkfs,以确保我们正在 fsck 合理的内容,您将执行以下操作

mkfs.btrfs -f /dev/mapper/log
dmsetup message log 0 mark mkfs
<run test>

这将允许您重放日志直到 mkfs 标记,然后从该点开始重放,并在您想要的间隔内执行 fsck 检查。

每个日志的末尾都有一个标记,标记为“dm-log-writes-end”。

用户空间组件

有一个用户空间工具可以以各种方式为您重放日志。可以在此处找到:https://github.com/josefbacik/log-writes

用法示例

假设您想在文件系统上测试 fsync。您将执行以下操作

TABLE="0 $(blockdev --getsz /dev/sdb) log-writes /dev/sdb /dev/sdc"
dmsetup create log --table "$TABLE"
mkfs.btrfs -f /dev/mapper/log
dmsetup message log 0 mark mkfs

mount /dev/mapper/log /mnt/btrfs-test
<some test that does fsync at the end>
dmsetup message log 0 mark fsync
md5sum /mnt/btrfs-test/foo
umount /mnt/btrfs-test

dmsetup remove log
replay-log --log /dev/sdc --replay /dev/sdb --end-mark fsync
mount /dev/sdb /mnt/btrfs-test
md5sum /mnt/btrfs-test/foo
<verify md5sum's are correct>

Another option is to do a complicated file system operation and verify the file
system is consistent during the entire operation.  You could do this with:

TABLE="0 $(blockdev --getsz /dev/sdb) log-writes /dev/sdb /dev/sdc"
dmsetup create log --table "$TABLE"
mkfs.btrfs -f /dev/mapper/log
dmsetup message log 0 mark mkfs

mount /dev/mapper/log /mnt/btrfs-test
<fsstress to dirty the fs>
btrfs filesystem balance /mnt/btrfs-test
umount /mnt/btrfs-test
dmsetup remove log

replay-log --log /dev/sdc --replay /dev/sdb --end-mark mkfs
btrfsck /dev/sdb
replay-log --log /dev/sdc --replay /dev/sdb --start-mark mkfs \
      --fsck "btrfsck /dev/sdb" --check fua

这将重放日志,直到看到 FUA 请求,运行 fsck 命令,如果 fsck 通过,它将重放到下一个 FUA,直到完成或 fsck 命令异常退出。