← Documents Documentation/admin-guide/device-mapper/log-writes.rst GitHub 원문 ↗

Linux 6.18.37 · Administration / Device Mapper

dm-log-writes

Filesystem write를 별도 장치에 data와 ordering까지 기록하고 flush·FUA 경계에서 정확히 replay해 crash consistency를 검증하는 target입니다.

Source pathDocumentation/admin-guide/device-mapper/log-writes.rst
Source versionLinux v6.18.37
TranslationDUJINLABS 전문 번역 + 해설

요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.

1. 요약·해설

원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.

Write journal과 ordering

log-writes.rst:1-53

정상 I/O와 replay log를 분리하고 completion·flush 순서에 따라 WRITE·DISCARD·FUA를 기록합니다.

Target 제어와 mark

log-writes.rst:54-100

Constructor, status, userspace mark와 replay tool 위치를 설명합니다.

Replay 기반 검증

log-writes.rst:101-145

fsync checksum 비교와 FUA 경계별 fsck로 durability와 consistency를 검사합니다.

2. 영어 원문 전체

번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.

원문 전체 펼치기
1 =============
2 dm-log-writes
3 =============
4
5 This target takes 2 devices, one to pass all IO to normally, and one to log all
6 of the write operations to. This is intended for file system developers wishing
7 to verify the integrity of metadata or data as the file system is written to.
8 There is a log_write_entry written for every WRITE request and the target is
9 able to take arbitrary data from userspace to insert into the log. The data
10 that is in the WRITE requests is copied into the log to make the replay happen
11 exactly as it happened originally.
12
13 Log Ordering
14 ============
15
16 We log things in order of completion once we are sure the write is no longer in
17 cache. This means that normal WRITE requests are not actually logged until the
18 next REQ_PREFLUSH request. This is to make it easier for userspace to replay
19 the log in a way that correlates to what is on disk and not what is in cache,
20 to make it easier to detect improper waiting/flushing.
21
22 This works by attaching all WRITE requests to a list once the write completes.
23 Once we see a REQ_PREFLUSH request we splice this list onto the request and once
24 the FLUSH request completes we log all of the WRITEs and then the FLUSH. Only
25 completed WRITEs, at the time the REQ_PREFLUSH is issued, are added in order to
26 simulate the worst case scenario with regard to power failures. Consider the
27 following example (W means write, C means complete):
28
29 W1,W2,W3,C3,C2,Wflush,C1,Cflush
30
31 The log would show the following:
32
33 W3,W2,flush,W1....
34
35 Again this is to simulate what is actually on disk, this allows us to detect
36 cases where a power failure at a particular point in time would create an
37 inconsistent file system.
38
39 Any REQ_FUA requests bypass this flushing mechanism and are logged as soon as
40 they complete as those requests will obviously bypass the device cache.
41
42 Any REQ_OP_DISCARD requests are treated like WRITE requests. Otherwise we would
43 have all the DISCARD requests, and then the WRITE requests and then the FLUSH
44 request. Consider the following example:
45
46 WRITE block 1, DISCARD block 1, FLUSH
47
48 If we logged DISCARD when it completed, the replay would look like this:
49
50 DISCARD 1, WRITE 1, FLUSH
51
52 which isn't quite what happened and wouldn't be caught during the log replay.
53
54 Target interface
55 ================
56
57 i) Constructor
58
59 log-writes <dev_path> <log_dev_path>
60
61 ============= ==============================================
62 dev_path Device that all of the IO will go to normally.
63 log_dev_path Device where the log entries are written to.
64 ============= ==============================================
65
66 ii) Status
67
68 <#logged entries> <highest allocated sector>
69
70 =========================== ========================
71 #logged entries Number of logged entries
72 highest allocated sector Highest allocated sector
73 =========================== ========================
74
75 iii) Messages
76
77 mark <description>
78
79 You can use a dmsetup message to set an arbitrary mark in a log.
80 For example say you want to fsck a file system after every
81 write, but first you need to replay up to the mkfs to make sure
82 we're fsck'ing something reasonable, you would do something like
83 this::
84
85 mkfs.btrfs -f /dev/mapper/log
86 dmsetup message log 0 mark mkfs
87 <run test>
88
89 This would allow you to replay the log up to the mkfs mark and
90 then replay from that point on doing the fsck check in the
91 interval that you want.
92
93 Every log has a mark at the end labeled "dm-log-writes-end".
94
95 Userspace component
96 ===================
97
98 There is a userspace tool that will replay the log for you in various ways.
99 It can be found here: https://github.com/josefbacik/log-writes
100
101 Example usage
102 =============
103
104 Say you want to test fsync on your file system. You would do something like
105 this::
106
107 TABLE="0 $(blockdev --getsz /dev/sdb) log-writes /dev/sdb /dev/sdc"
108 dmsetup create log --table "$TABLE"
109 mkfs.btrfs -f /dev/mapper/log
110 dmsetup message log 0 mark mkfs
111
112 mount /dev/mapper/log /mnt/btrfs-test
113 <some test that does fsync at the end>
114 dmsetup message log 0 mark fsync
115 md5sum /mnt/btrfs-test/foo
116 umount /mnt/btrfs-test
117
118 dmsetup remove log
119 replay-log --log /dev/sdc --replay /dev/sdb --end-mark fsync
120 mount /dev/sdb /mnt/btrfs-test
121 md5sum /mnt/btrfs-test/foo
122 <verify md5sum's are correct>
123
124 Another option is to do a complicated file system operation and verify the file
125 system is consistent during the entire operation. You could do this with:
126
127 TABLE="0 $(blockdev --getsz /dev/sdb) log-writes /dev/sdb /dev/sdc"
128 dmsetup create log --table "$TABLE"
129 mkfs.btrfs -f /dev/mapper/log
130 dmsetup message log 0 mark mkfs
131
132 mount /dev/mapper/log /mnt/btrfs-test
133 <fsstress to dirty the fs>
134 btrfs filesystem balance /mnt/btrfs-test
135 umount /mnt/btrfs-test
136 dmsetup remove log
137
138 replay-log --log /dev/sdc --replay /dev/sdb --end-mark mkfs
139 btrfsck /dev/sdb
140 replay-log --log /dev/sdc --replay /dev/sdb --start-mark mkfs \
141 --fsck "btrfsck /dev/sdb" --check fua
142
143 And that will replay the log until it sees a FUA request, run the fsck command
144 and if the fsck passes it will replay to the next FUA, until it is completed or
145 the fsck command exists abnormally.
146

3. 한국어 전문 번역

영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.

실제 I/O 장치와 write journal 장치

1-12

`dm-log-writes` target은 두 장치를 받습니다. 한 장치에는 모든 I/O를 정상적으로 전달하고, 다른 장치에는 모든 write operation을 기록합니다. Filesystem을 write하는 동안 metadata 또는 data integrity를 검증하려는 filesystem developer를 위한 기능입니다.

각 `WRITE` request마다 `log_write_entry` 하나를 기록하며, userspace가 임의 data를 log에 삽입할 수도 있습니다. Replay가 원래 실행과 정확히 같도록 `WRITE` request에 들어 있던 data 자체도 log에 복사합니다.

dm-log-writes 데이터 경로
Filesystem I/O`dm-log-writes`정상 I/O 장치
`WRITE` data + ordering + userspace mark`log_write_entry`Log 장치

정상 I/O는 대상 장치로 전달하면서 write 내용과 ordering 정보는 별도 log 장치에 남깁니다.

두 backing device의 역할
장치내용목적
`dev_path`정상적인 모든 I/O검사할 filesystem 상태
`log_dev_path`Write entry와 write data동일한 실행 replay

실제 filesystem 상태와 재현용 journal을 물리적으로 분리합니다.

Completion과 REQ_PREFLUSH에 맞춘 기록 순서

13-40

Write가 더 이상 cache에 없다고 확신한 뒤 completion 순서로 항목을 기록합니다. 따라서 일반 `WRITE` request는 실제로 다음 `REQ_PREFLUSH` request가 올 때까지 log에 기록되지 않습니다. Userspace replay가 cache 안의 상태가 아니라 disk의 상태와 대응하게 하여 잘못된 wait 또는 flush 처리를 찾기 쉽게 합니다.

각 `WRITE`가 완료되면 request를 목록에 연결합니다. `REQ_PREFLUSH`를 만나면 그 시점의 완료 목록을 해당 request에 splice합니다. `FLUSH`가 완료된 뒤 연결된 모든 `WRITE`를 먼저 기록하고 마지막에 `FLUSH`를 기록합니다. 전원 장애의 최악 조건을 모사하기 위해 `REQ_PREFLUSH` 발행 시점에 이미 완료된 `WRITE`만 포함합니다.

	W1,W2,W3,C3,C2,Wflush,C1,Cflush
	W3,W2,flush,W1....
Completion 기반 flush logging
`W1,W2,W3,C3,C2``Wflush` 발행완료 목록: W3, W2`Cflush`Log: `W3,W2,flush`
`C1`은 preflush 이후 완료현재 flush 목록에서 제외다음 flush까지 대기Log 뒤쪽의 `W1....`

예제에서 C3와 C2는 preflush 전에 끝났지만 C1은 이후에 끝나므로 다음 기록 구간으로 넘어갑니다.

이 순서는 disk에 실제로 존재할 수 있는 상태를 모사하므로, 특정 시점의 전원 장애가 inconsistent filesystem을 만드는 경우를 탐지할 수 있습니다.

`REQ_FUA` request는 device cache를 우회하므로 이 flush 대기 메커니즘도 건너뛰며, request가 완료되는 즉시 기록됩니다.

DISCARD와 WRITE의 상대 순서 보존

41-53

`REQ_OP_DISCARD` request는 `WRITE` request와 같은 방식으로 처리합니다. 그렇지 않으면 모든 `DISCARD`, 모든 `WRITE`, 마지막 `FLUSH`의 순서로 뭉쳐 기록되어 실제 operation ordering이 달라질 수 있습니다.

	WRITE block 1, DISCARD block 1, FLUSH

위 작업에서 `DISCARD`를 완료 즉시 별도로 기록하면 replay 순서는 다음처럼 바뀝니다.

	DISCARD 1, WRITE 1, FLUSH

이 replay 결과는 실제 실행과 다르지만 log replay 검사에서는 차이를 잡지 못하게 됩니다. 따라서 `DISCARD`도 일반 `WRITE`와 같은 completion·flush ordering에 포함해야 합니다.

DISCARD ordering 비교
실제`WRITE block 1``DISCARD block 1``FLUSH`
잘못된 replay`DISCARD 1``WRITE 1``FLUSH`

같은 block의 write와 discard 순서가 뒤집히면 최종 block 상태도 달라질 수 있습니다.

Constructor와 status

54-74

Constructor는 정상 I/O를 받을 장치와 log entry를 저장할 장치의 경로를 차례로 받습니다.

i) Constructor

   log-writes <dev_path> <log_dev_path>

   ============= ==============================================
   dev_path	 Device that all of the IO will go to normally.
   log_dev_path  Device where the log entries are written to.
   ============= ==============================================
Constructor parameter
Parameter의미
`dev_path`모든 I/O가 정상적으로 전달되는 장치
`log_dev_path`Log entry가 기록되는 장치

`log-writes` target을 구성하는 두 device path입니다.

Status는 지금까지 기록된 entry 수와 log 장치에서 할당된 가장 높은 sector를 보고합니다.

ii) Status

    <#logged entries> <highest allocated sector>

    =========================== ========================
    #logged entries	        Number of logged entries
    highest allocated sector    Highest allocated sector
    =========================== ========================
Status field
Field의미
`#logged entries`기록된 entry 수
`highest allocated sector`할당된 가장 높은 sector

Log의 논리 entry 수와 물리 allocation 진행 위치를 함께 확인합니다.

Userspace mark와 자동 종료 mark

75-94

`mark <description>` message를 사용하면 `dmsetup message`로 log의 임의 지점에 mark를 설정할 수 있습니다.

    mark <description>

예를 들어 매 write 뒤에 filesystem을 `fsck`하되 먼저 `mkfs` 지점까지 replay해 검사 가능한 초기 filesystem을 만들고 싶다면 다음처럼 사용합니다.

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

이 mark를 이용하면 log를 `mkfs`까지 먼저 replay하고, 그 이후부터 원하는 간격으로 replay하며 `fsck`를 수행할 수 있습니다. 모든 log의 끝에는 `dm-log-writes-end`라는 mark가 자동으로 존재합니다.

Mark 기반 replay 구간
`mkfs.btrfs``mark mkfs`Test write 구간원하는 지점까지 replay반복 `fsck` 검사`dm-log-writes-end`

재현의 기준점과 검사 구간을 이름이 있는 mark로 분리합니다.

Replay userspace 도구

95-100

Log를 여러 방식으로 replay하는 userspace tool이 제공되며 다음 project에서 구할 수 있습니다.

It can be found here: https://github.com/josefbacik/log-writes
Userspace replay project
ProjectURL
`log-writes``https://github.com/josefbacik/log-writes`

`dm-log-writes` journal을 실제 block device에 다시 적용하는 도구입니다.

fsync 결과를 checksum으로 검증

101-123

Filesystem의 `fsync` 동작을 시험하려면 `/dev/sdb`를 정상 I/O 장치, `/dev/sdc`를 log 장치로 하는 target을 만듭니다. Btrfs를 생성한 직후 `mkfs` mark를 남기고, mount한 filesystem에서 마지막에 `fsync`를 수행하는 test를 실행한 뒤 `fsync` mark와 원본 file의 `md5sum`을 기록합니다.

Target을 제거한 뒤 `replay-log`로 `/dev/sdc`의 log를 `fsync` mark까지 `/dev/sdb`에 replay합니다. 재현된 filesystem을 다시 mount해 같은 file의 `md5sum`을 비교하면 `fsync` 시점의 data가 올바르게 지속됐는지 확인할 수 있습니다.

  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>
fsync durability 검사
Target 생성 + Btrfs format`mark mkfs`fsync test`mark fsync`첫 `md5sum`fsync mark까지 replay둘째 `md5sum` 비교

원래 실행의 fsync 시점과 replay된 같은 시점의 file checksum을 비교합니다.

복잡한 filesystem 작업 전 구간 검사

124-145

또 다른 방법은 복잡한 filesystem operation 전체에서 filesystem consistency를 검사하는 것입니다. 예제는 target과 Btrfs를 만들고 `mkfs` mark를 남긴 뒤 `fsstress`로 filesystem을 dirty하게 만들고 Btrfs balance를 실행합니다.

먼저 log를 `mkfs` mark까지 replay하고 `btrfsck`로 초기 상태를 검사합니다. 그다음 `--start-mark mkfs`, `--fsck "btrfsck /dev/sdb"`, `--check fua`를 사용해 `FUA` request를 만날 때까지 replay하고 매 지점마다 fsck command를 실행합니다.

  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

각 fsck가 통과하면 다음 `FUA`까지 replay를 계속합니다. 전체 replay가 끝나거나 fsck command가 비정상 종료할 때 반복을 멈춥니다.

FUA 경계별 consistency 검사
`mkfs` mark까지 replay초기 `btrfsck`다음 `FUA`까지 replay`btrfsck /dev/sdb`통과 시 다음 FUA완료 또는 비정상 종료

Durability boundary마다 replay를 멈추고 filesystem checker를 실행합니다.