dm-log-writes¶
此目標需要兩個裝置:一個用於正常處理所有 I/O,另一個用於記錄所有寫入操作。這旨在幫助檔案系統開發人員驗證檔案系統寫入時元資料或資料的完整性。每個 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 請求。考慮以下示例
WRITE 塊 1, DISCARD 塊 1, FLUSH
如果我們在 DISCARD 完成時記錄它,重放將如下所示
DISCARD 1, WRITE 1, FLUSH
這與實際發生的情況不完全一致,並且在日誌重放期間不會被發現。
目標介面¶
建構函式
log-writes <dev_path> <log_dev_path>
dev_path
所有 I/O 正常流向的裝置。
log_dev_path
日誌條目寫入的裝置。
狀態
<#已記錄條目> <最高分配扇區>
#已記錄條目
已記錄條目數
最高分配扇區
最高分配扇區
訊息
mark <描述>
您可以使用 dmsetup 訊息在日誌中設定任意標記。例如,假設您想在每次寫入後對檔案系統進行 fsck 檢查,但首先需要重放到 mkfs 處,以確保我們檢查的是合理的檔案系統,您可以這樣做:
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 命令異常退出。