← Documents Documentation/filesystems/ext4/journal.rst GitHub 원문 ↗

Linux 6.18.37 · Filesystems

Journal (jbd2)

ext4 JBD2 journal 배치, block 형식, checksum, fast commit replay와 checkpoint의 전문 번역입니다.

Source pathDocumentation/filesystems/ext4/journal.rst
Source versionLinux v6.18.37
TranslationDUJINLABS 전문 번역 + 해설

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

1. 요약·해설

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

요약·해설

journal.rst:1-761

ext4는 JBD2 journal에 metadata transaction을 먼저 기록하고 commit block으로 완료를 확정한 뒤 최종 위치로 checkpoint합니다. crash 뒤에는 checksum이 맞고 commit된 transaction까지만 replay하므로 metadata update의 atomicity를 보장합니다.

모든 JBD2 on-disk field는 big-endian입니다. descriptor tag는 뒤따르는 data block의 최종 위치와 checksum을 기록하고, revoke record는 재할당된 block에 오래된 metadata를 덮어쓰는 일을 방지합니다.

fast commit은 `data=ordered` mode에서 metadata block 전체 대신 최종 상태를 재구성하는 TLV delta를 기록합니다. 절차가 아닌 idempotent outcome을 저장해 recovery가 반복되어도 같은 상태로 수렴합니다.

JBD2 recovery 관점의 전체 흐름
Descriptor·data 또는 revoke block 기록block·metadata checksum 검증 정보 추가commit block으로 transaction 완료 확정정상 동작에서는 최종 위치로 checkpointcrash 시 마지막 유효 commit까지만 replayrevoke된 block은 건너뛰고 escaped data는 복원fast commit TLV는 결과 상태를 멱등하게 적용

transaction 기록부터 replay와 checkpoint까지의 핵심 경로입니다.

2. 영어 원문 전체

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

원문 전체 펼치기
1 .. SPDX-License-Identifier: GPL-2.0
2
3 Journal (jbd2)
4 --------------
5
6 Introduced in ext3, the ext4 filesystem employs a journal to protect the
7 filesystem against metadata inconsistencies in the case of a system crash. Up
8 to 10,240,000 file system blocks (see man mke2fs(8) for more details on journal
9 size limits) can be reserved inside the filesystem as a place to land
10 “important” data writes on-disk as quickly as possible. Once the important
11 data transaction is fully written to the disk and flushed from the disk write
12 cache, a record of the data being committed is also written to the journal. At
13 some later point in time, the journal code writes the transactions to their
14 final locations on disk (this could involve a lot of seeking or a lot of small
15 read-write-erases) before erasing the commit record. Should the system
16 crash during the second slow write, the journal can be replayed all the
17 way to the latest commit record, guaranteeing the atomicity of whatever
18 gets written through the journal to the disk. The effect of this is to
19 guarantee that the filesystem does not become stuck midway through a
20 metadata update.
21
22 For performance reasons, ext4 by default only writes filesystem metadata
23 through the journal. This means that file data blocks are /not/
24 guaranteed to be in any consistent state after a crash. If this default
25 guarantee level (``data=ordered``) is not satisfactory, there is a mount
26 option to control journal behavior. If ``data=journal``, all data and
27 metadata are written to disk through the journal. This is slower but
28 safest. If ``data=writeback``, dirty data blocks are not flushed to the
29 disk before the metadata are written to disk through the journal.
30
31 In case of ``data=ordered`` mode, Ext4 also supports fast commits which
32 help reduce commit latency significantly. The default ``data=ordered``
33 mode works by logging metadata blocks to the journal. In fast commit
34 mode, Ext4 only stores the minimal delta needed to recreate the
35 affected metadata in fast commit space that is shared with JBD2.
36 Once the fast commit area fills in or if fast commit is not possible
37 or if JBD2 commit timer goes off, Ext4 performs a traditional full commit.
38 A full commit invalidates all the fast commits that happened before
39 it and thus it makes the fast commit area empty for further fast
40 commits. This feature needs to be enabled at mkfs time.
41
42 The journal inode is typically inode 8. The first 68 bytes of the
43 journal inode are replicated in the ext4 superblock. The journal itself
44 is normal (but hidden) file within the filesystem. The file usually
45 consumes an entire block group, though mke2fs tries to put it in the
46 middle of the disk.
47
48 All fields in jbd2 are written to disk in big-endian order. This is the
49 opposite of ext4.
50
51 NOTE: Both ext4 and ocfs2 use jbd2.
52
53 The maximum size of a journal embedded in an ext4 filesystem is 2^32
54 blocks. jbd2 itself does not seem to care.
55
56 Layout
57 ~~~~~~
58
59 Generally speaking, the journal has this format:
60
61 .. list-table::
62 :widths: 16 48 16
63 :header-rows: 1
64
65 * - Superblock
66 - descriptor_block (data_blocks or revocation_block) [more data or
67 revocations] commmit_block
68 - [more transactions...]
69 * -
70 - One transaction
71 -
72
73 Notice that a transaction begins with either a descriptor and some data,
74 or a block revocation list. A finished transaction always ends with a
75 commit. If there is no commit record (or the checksums don't match), the
76 transaction will be discarded during replay.
77
78 External Journal
79 ~~~~~~~~~~~~~~~~
80
81 Optionally, an ext4 filesystem can be created with an external journal
82 device (as opposed to an internal journal, which uses a reserved inode).
83 In this case, on the filesystem device, ``s_journal_inum`` should be
84 zero and ``s_journal_uuid`` should be set. On the journal device there
85 will be an ext4 super block in the usual place, with a matching UUID.
86 The journal superblock will be in the next full block after the
87 superblock.
88
89 .. list-table::
90 :widths: 12 12 12 32 12
91 :header-rows: 1
92
93 * - 1024 bytes of padding
94 - ext4 Superblock
95 - Journal Superblock
96 - descriptor_block (data_blocks or revocation_block) [more data or
97 revocations] commmit_block
98 - [more transactions...]
99 * -
100 -
101 -
102 - One transaction
103 -
104
105 Block Header
106 ~~~~~~~~~~~~
107
108 Every block in the journal starts with a common 12-byte header
109 ``struct journal_header_s``:
110
111 .. list-table::
112 :widths: 8 8 24 40
113 :header-rows: 1
114
115 * - Offset
116 - Type
117 - Name
118 - Description
119 * - 0x0
120 - __be32
121 - h_magic
122 - jbd2 magic number, 0xC03B3998.
123 * - 0x4
124 - __be32
125 - h_blocktype
126 - Description of what this block contains. See the jbd2_blocktype_ table
127 below.
128 * - 0x8
129 - __be32
130 - h_sequence
131 - The transaction ID that goes with this block.
132
133 .. _jbd2_blocktype:
134
135 The journal block type can be any one of:
136
137 .. list-table::
138 :widths: 16 64
139 :header-rows: 1
140
141 * - Value
142 - Description
143 * - 1
144 - Descriptor. This block precedes a series of data blocks that were
145 written through the journal during a transaction.
146 * - 2
147 - Block commit record. This block signifies the completion of a
148 transaction.
149 * - 3
150 - Journal superblock, v1.
151 * - 4
152 - Journal superblock, v2.
153 * - 5
154 - Block revocation records. This speeds up recovery by enabling the
155 journal to skip writing blocks that were subsequently rewritten.
156
157 Super Block
158 ~~~~~~~~~~~
159
160 The super block for the journal is much simpler as compared to ext4's.
161 The key data kept within are size of the journal, and where to find the
162 start of the log of transactions.
163
164 The journal superblock is recorded as ``struct journal_superblock_s``,
165 which is 1024 bytes long:
166
167 .. list-table::
168 :widths: 8 8 24 40
169 :header-rows: 1
170
171 * - Offset
172 - Type
173 - Name
174 - Description
175 * -
176 -
177 -
178 - Static information describing the journal.
179 * - 0x0
180 - journal_header_t (12 bytes)
181 - s_header
182 - Common header identifying this as a superblock.
183 * - 0xC
184 - __be32
185 - s_blocksize
186 - Journal device block size.
187 * - 0x10
188 - __be32
189 - s_maxlen
190 - Total number of blocks in this journal.
191 * - 0x14
192 - __be32
193 - s_first
194 - First block of log information.
195 * -
196 -
197 -
198 - Dynamic information describing the current state of the log.
199 * - 0x18
200 - __be32
201 - s_sequence
202 - First commit ID expected in log.
203 * - 0x1C
204 - __be32
205 - s_start
206 - Block number of the start of log. Contrary to the comments, this field
207 being zero does not imply that the journal is clean!
208 * - 0x20
209 - __be32
210 - s_errno
211 - Error value, as set by jbd2_journal_abort().
212 * -
213 -
214 -
215 - The remaining fields are only valid in a v2 superblock.
216 * - 0x24
217 - __be32
218 - s_feature_compat;
219 - Compatible feature set. See the table jbd2_compat_ below.
220 * - 0x28
221 - __be32
222 - s_feature_incompat
223 - Incompatible feature set. See the table jbd2_incompat_ below.
224 * - 0x2C
225 - __be32
226 - s_feature_ro_compat
227 - Read-only compatible feature set. There aren't any of these currently.
228 * - 0x30
229 - __u8
230 - s_uuid[16]
231 - 128-bit uuid for journal. This is compared against the copy in the ext4
232 super block at mount time.
233 * - 0x40
234 - __be32
235 - s_nr_users
236 - Number of file systems sharing this journal.
237 * - 0x44
238 - __be32
239 - s_dynsuper
240 - Location of dynamic super block copy. (Not used?)
241 * - 0x48
242 - __be32
243 - s_max_transaction
244 - Limit of journal blocks per transaction. (Not used?)
245 * - 0x4C
246 - __be32
247 - s_max_trans_data
248 - Limit of data blocks per transaction. (Not used?)
249 * - 0x50
250 - __u8
251 - s_checksum_type
252 - Checksum algorithm used for the journal. See jbd2_checksum_type_ for
253 more info.
254 * - 0x51
255 - __u8[3]
256 - s_padding2
257 -
258 * - 0x54
259 - __be32
260 - s_num_fc_blocks
261 - Number of fast commit blocks in the journal.
262 * - 0x58
263 - __be32
264 - s_head
265 - Block number of the head (first unused block) of the journal, only
266 up-to-date when the journal is empty.
267 * - 0x5C
268 - __u32
269 - s_padding[40]
270 -
271 * - 0xFC
272 - __be32
273 - s_checksum
274 - Checksum of the entire superblock, with this field set to zero.
275 * - 0x100
276 - __u8
277 - s_users[16*48]
278 - ids of all file systems sharing the log. e2fsprogs/Linux don't allow
279 shared external journals, but I imagine Lustre (or ocfs2?), which use
280 the jbd2 code, might.
281
282 .. _jbd2_compat:
283
284 The journal compat features are any combination of the following:
285
286 .. list-table::
287 :widths: 16 64
288 :header-rows: 1
289
290 * - Value
291 - Description
292 * - 0x1
293 - Journal maintains checksums on the data blocks.
294 (JBD2_FEATURE_COMPAT_CHECKSUM)
295
296 .. _jbd2_incompat:
297
298 The journal incompat features are any combination of the following:
299
300 .. list-table::
301 :widths: 16 64
302 :header-rows: 1
303
304 * - Value
305 - Description
306 * - 0x1
307 - Journal has block revocation records. (JBD2_FEATURE_INCOMPAT_REVOKE)
308 * - 0x2
309 - Journal can deal with 64-bit block numbers.
310 (JBD2_FEATURE_INCOMPAT_64BIT)
311 * - 0x4
312 - Journal commits asynchronously. (JBD2_FEATURE_INCOMPAT_ASYNC_COMMIT)
313 * - 0x8
314 - This journal uses v2 of the checksum on-disk format. Each journal
315 metadata block gets its own checksum, and the block tags in the
316 descriptor table contain checksums for each of the data blocks in the
317 journal. (JBD2_FEATURE_INCOMPAT_CSUM_V2)
318 * - 0x10
319 - This journal uses v3 of the checksum on-disk format. This is the same as
320 v2, but the journal block tag size is fixed regardless of the size of
321 block numbers. (JBD2_FEATURE_INCOMPAT_CSUM_V3)
322 * - 0x20
323 - Journal has fast commit blocks. (JBD2_FEATURE_INCOMPAT_FAST_COMMIT)
324
325 .. _jbd2_checksum_type:
326
327 Journal checksum type codes are one of the following. crc32 or crc32c are the
328 most likely choices.
329
330 .. list-table::
331 :widths: 16 64
332 :header-rows: 1
333
334 * - Value
335 - Description
336 * - 1
337 - CRC32
338 * - 2
339 - MD5
340 * - 3
341 - SHA1
342 * - 4
343 - CRC32C
344
345 Descriptor Block
346 ~~~~~~~~~~~~~~~~
347
348 The descriptor block contains an array of journal block tags that
349 describe the final locations of the data blocks that follow in the
350 journal. Descriptor blocks are open-coded instead of being completely
351 described by a data structure, but here is the block structure anyway.
352 Descriptor blocks consume at least 36 bytes, but use a full block:
353
354 .. list-table::
355 :widths: 8 8 24 40
356 :header-rows: 1
357
358 * - Offset
359 - Type
360 - Name
361 - Descriptor
362 * - 0x0
363 - journal_header_t
364 - (open coded)
365 - Common block header.
366 * - 0xC
367 - struct journal_block_tag_s
368 - open coded array[]
369 - Enough tags either to fill up the block or to describe all the data
370 blocks that follow this descriptor block.
371
372 Journal block tags have any of the following formats, depending on which
373 journal feature and block tag flags are set.
374
375 If JBD2_FEATURE_INCOMPAT_CSUM_V3 is set, the journal block tag is
376 defined as ``struct journal_block_tag3_s``, which looks like the
377 following. The size is 16 or 32 bytes.
378
379 .. list-table::
380 :widths: 8 8 24 40
381 :header-rows: 1
382
383 * - Offset
384 - Type
385 - Name
386 - Descriptor
387 * - 0x0
388 - __be32
389 - t_blocknr
390 - Lower 32-bits of the location of where the corresponding data block
391 should end up on disk.
392 * - 0x4
393 - __be32
394 - t_flags
395 - Flags that go with the descriptor. See the table jbd2_tag_flags_ for
396 more info.
397 * - 0x8
398 - __be32
399 - t_blocknr_high
400 - Upper 32-bits of the location of where the corresponding data block
401 should end up on disk. This is zero if JBD2_FEATURE_INCOMPAT_64BIT is
402 not enabled.
403 * - 0xC
404 - __be32
405 - t_checksum
406 - Checksum of the journal UUID, the sequence number, and the data block.
407 * -
408 -
409 -
410 - This field appears to be open coded. It always comes at the end of the
411 tag, after t_checksum. This field is not present if the "same UUID" flag
412 is set.
413 * - 0x8 or 0xC
414 - char
415 - uuid[16]
416 - A UUID to go with this tag. This field appears to be copied from the
417 ``j_uuid`` field in ``struct journal_s``, but only tune2fs touches that
418 field.
419
420 .. _jbd2_tag_flags:
421
422 The journal tag flags are any combination of the following:
423
424 .. list-table::
425 :widths: 16 64
426 :header-rows: 1
427
428 * - Value
429 - Description
430 * - 0x1
431 - On-disk block is escaped. The first four bytes of the data block just
432 happened to match the jbd2 magic number.
433 * - 0x2
434 - This block has the same UUID as previous, therefore the UUID field is
435 omitted.
436 * - 0x4
437 - The data block was deleted by the transaction. (Not used?)
438 * - 0x8
439 - This is the last tag in this descriptor block.
440
441 If JBD2_FEATURE_INCOMPAT_CSUM_V3 is NOT set, the journal block tag
442 is defined as ``struct journal_block_tag_s``, which looks like the
443 following. The size is 8, 12, 24, or 28 bytes:
444
445 .. list-table::
446 :widths: 8 8 24 40
447 :header-rows: 1
448
449 * - Offset
450 - Type
451 - Name
452 - Descriptor
453 * - 0x0
454 - __be32
455 - t_blocknr
456 - Lower 32-bits of the location of where the corresponding data block
457 should end up on disk.
458 * - 0x4
459 - __be16
460 - t_checksum
461 - Checksum of the journal UUID, the sequence number, and the data block.
462 Note that only the lower 16 bits are stored.
463 * - 0x6
464 - __be16
465 - t_flags
466 - Flags that go with the descriptor. See the table jbd2_tag_flags_ for
467 more info.
468 * -
469 -
470 -
471 - This next field is only present if the super block indicates support for
472 64-bit block numbers.
473 * - 0x8
474 - __be32
475 - t_blocknr_high
476 - Upper 32-bits of the location of where the corresponding data block
477 should end up on disk.
478 * -
479 -
480 -
481 - This field appears to be open coded. It always comes at the end of the
482 tag, after t_flags or t_blocknr_high. This field is not present if the
483 "same UUID" flag is set.
484 * - 0x8 or 0xC
485 - char
486 - uuid[16]
487 - A UUID to go with this tag. This field appears to be copied from the
488 ``j_uuid`` field in ``struct journal_s``, but only tune2fs touches that
489 field.
490
491 If JBD2_FEATURE_INCOMPAT_CSUM_V2 or
492 JBD2_FEATURE_INCOMPAT_CSUM_V3 are set, the end of the block is a
493 ``struct jbd2_journal_block_tail``, which looks like this:
494
495 .. list-table::
496 :widths: 8 8 24 40
497 :header-rows: 1
498
499 * - Offset
500 - Type
501 - Name
502 - Descriptor
503 * - 0x0
504 - __be32
505 - t_checksum
506 - Checksum of the journal UUID + the descriptor block, with this field set
507 to zero.
508
509 Data Block
510 ~~~~~~~~~~
511
512 In general, the data blocks being written to disk through the journal
513 are written verbatim into the journal file after the descriptor block.
514 However, if the first four bytes of the block match the jbd2 magic
515 number then those four bytes are replaced with zeroes and the “escaped”
516 flag is set in the descriptor block tag.
517
518 Revocation Block
519 ~~~~~~~~~~~~~~~~
520
521 A revocation block is used to prevent replay of a block in an earlier
522 transaction. This is used to mark blocks that were journalled at one
523 time but are no longer journalled. Typically this happens if a metadata
524 block is freed and re-allocated as a file data block; in this case, a
525 journal replay after the file block was written to disk will cause
526 corruption.
527
528 **NOTE**: This mechanism is NOT used to express “this journal block is
529 superseded by this other journal block”, as the author (djwong)
530 mistakenly thought. Any block being added to a transaction will cause
531 the removal of all existing revocation records for that block.
532
533 Revocation blocks are described in
534 ``struct jbd2_journal_revoke_header_s``, are at least 16 bytes in
535 length, but use a full block:
536
537 .. list-table::
538 :widths: 8 8 24 40
539 :header-rows: 1
540
541 * - Offset
542 - Type
543 - Name
544 - Description
545 * - 0x0
546 - journal_header_t
547 - r_header
548 - Common block header.
549 * - 0xC
550 - __be32
551 - r_count
552 - Number of bytes used in this block.
553 * - 0x10
554 - __be32 or __be64
555 - blocks[0]
556 - Blocks to revoke.
557
558 After r_count is a linear array of block numbers that are effectively
559 revoked by this transaction. The size of each block number is 8 bytes if
560 the superblock advertises 64-bit block number support, or 4 bytes
561 otherwise.
562
563 If JBD2_FEATURE_INCOMPAT_CSUM_V2 or
564 JBD2_FEATURE_INCOMPAT_CSUM_V3 are set, the end of the revocation
565 block is a ``struct jbd2_journal_revoke_tail``, which has this format:
566
567 .. list-table::
568 :widths: 8 8 24 40
569 :header-rows: 1
570
571 * - Offset
572 - Type
573 - Name
574 - Description
575 * - 0x0
576 - __be32
577 - r_checksum
578 - Checksum of the journal UUID + revocation block
579
580 Commit Block
581 ~~~~~~~~~~~~
582
583 The commit block is a sentry that indicates that a transaction has been
584 completely written to the journal. Once this commit block reaches the
585 journal, the data stored with this transaction can be written to their
586 final locations on disk.
587
588 The commit block is described by ``struct commit_header``, which is 32
589 bytes long (but uses a full block):
590
591 .. list-table::
592 :widths: 8 8 24 40
593 :header-rows: 1
594
595 * - Offset
596 - Type
597 - Name
598 - Descriptor
599 * - 0x0
600 - journal_header_s
601 - (open coded)
602 - Common block header.
603 * - 0xC
604 - unsigned char
605 - h_chksum_type
606 - The type of checksum to use to verify the integrity of the data blocks
607 in the transaction. See jbd2_checksum_type_ for more info.
608 * - 0xD
609 - unsigned char
610 - h_chksum_size
611 - The number of bytes used by the checksum. Most likely 4.
612 * - 0xE
613 - unsigned char
614 - h_padding[2]
615 -
616 * - 0x10
617 - __be32
618 - h_chksum[JBD2_CHECKSUM_BYTES]
619 - 32 bytes of space to store checksums. If
620 JBD2_FEATURE_INCOMPAT_CSUM_V2 or JBD2_FEATURE_INCOMPAT_CSUM_V3
621 are set, the first ``__be32`` is the checksum of the journal UUID and
622 the entire commit block, with this field zeroed. If
623 JBD2_FEATURE_COMPAT_CHECKSUM is set, the first ``__be32`` is the
624 crc32 of all the blocks already written to the transaction.
625 * - 0x30
626 - __be64
627 - h_commit_sec
628 - The time that the transaction was committed, in seconds since the epoch.
629 * - 0x38
630 - __be32
631 - h_commit_nsec
632 - Nanoseconds component of the above timestamp.
633
634 Fast commits
635 ~~~~~~~~~~~~
636
637 Fast commit area is organized as a log of tag length values. Each TLV has
638 a ``struct ext4_fc_tl`` in the beginning which stores the tag and the length
639 of the entire field. It is followed by variable length tag specific value.
640 Here is the list of supported tags and their meanings:
641
642 .. list-table::
643 :widths: 8 20 20 32
644 :header-rows: 1
645
646 * - Tag
647 - Meaning
648 - Value struct
649 - Description
650 * - EXT4_FC_TAG_HEAD
651 - Fast commit area header
652 - ``struct ext4_fc_head``
653 - Stores the TID of the transaction after which these fast commits should
654 be applied.
655 * - EXT4_FC_TAG_ADD_RANGE
656 - Add extent to inode
657 - ``struct ext4_fc_add_range``
658 - Stores the inode number and extent to be added in this inode
659 * - EXT4_FC_TAG_DEL_RANGE
660 - Remove logical offsets to inode
661 - ``struct ext4_fc_del_range``
662 - Stores the inode number and the logical offset range that needs to be
663 removed
664 * - EXT4_FC_TAG_CREAT
665 - Create directory entry for a newly created file
666 - ``struct ext4_fc_dentry_info``
667 - Stores the parent inode number, inode number and directory entry of the
668 newly created file
669 * - EXT4_FC_TAG_LINK
670 - Link a directory entry to an inode
671 - ``struct ext4_fc_dentry_info``
672 - Stores the parent inode number, inode number and directory entry
673 * - EXT4_FC_TAG_UNLINK
674 - Unlink a directory entry of an inode
675 - ``struct ext4_fc_dentry_info``
676 - Stores the parent inode number, inode number and directory entry
677
678 * - EXT4_FC_TAG_PAD
679 - Padding (unused area)
680 - None
681 - Unused bytes in the fast commit area.
682
683 * - EXT4_FC_TAG_TAIL
684 - Mark the end of a fast commit
685 - ``struct ext4_fc_tail``
686 - Stores the TID of the commit, CRC of the fast commit of which this tag
687 represents the end of
688
689 Fast Commit Replay Idempotence
690 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
691
692 Fast commits tags are idempotent in nature provided the recovery code follows
693 certain rules. The guiding principle that the commit path follows while
694 committing is that it stores the result of a particular operation instead of
695 storing the procedure.
696
697 Let's consider this rename operation: 'mv /a /b'. Let's assume dirent '/a'
698 was associated with inode 10. During fast commit, instead of storing this
699 operation as a procedure "rename a to b", we store the resulting file system
700 state as a "series" of outcomes:
701
702 - Link dirent b to inode 10
703 - Unlink dirent a
704 - Inode 10 with valid refcount
705
706 Now when recovery code runs, it needs "enforce" this state on the file
707 system. This is what guarantees idempotence of fast commit replay.
708
709 Let's take an example of a procedure that is not idempotent and see how fast
710 commits make it idempotent. Consider following sequence of operations:
711
712 1) rm A
713 2) mv B A
714 3) read A
715
716 If we store this sequence of operations as is then the replay is not idempotent.
717 Let's say while in replay, we crash after (2). During the second replay,
718 file A (which was actually created as a result of "mv B A" operation) would get
719 deleted. Thus, file named A would be absent when we try to read A. So, this
720 sequence of operations is not idempotent. However, as mentioned above, instead
721 of storing the procedure fast commits store the outcome of each procedure. Thus
722 the fast commit log for above procedure would be as follows:
723
724 (Let's assume dirent A was linked to inode 10 and dirent B was linked to
725 inode 11 before the replay)
726
727 1) Unlink A
728 2) Link A to inode 11
729 3) Unlink B
730 4) Inode 11
731
732 If we crash after (3) we will have file A linked to inode 11. During the second
733 replay, we will remove file A (inode 11). But we will create it back and make
734 it point to inode 11. We won't find B, so we'll just skip that step. At this
735 point, the refcount for inode 11 is not reliable, but that gets fixed by the
736 replay of last inode 11 tag. Thus, by converting a non-idempotent procedure
737 into a series of idempotent outcomes, fast commits ensured idempotence during
738 the replay.
739
740 Journal Checkpoint
741 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
742
743 Checkpointing the journal ensures all transactions and their associated buffers
744 are submitted to the disk. In-progress transactions are waited upon and included
745 in the checkpoint. Checkpointing is used internally during critical updates to
746 the filesystem including journal recovery, filesystem resizing, and freeing of
747 the journal_t structure.
748
749 A journal checkpoint can be triggered from userspace via the ioctl
750 EXT4_IOC_CHECKPOINT. This ioctl takes a single, u64 argument for flags.
751 Currently, three flags are supported. First, EXT4_IOC_CHECKPOINT_FLAG_DRY_RUN
752 can be used to verify input to the ioctl. It returns error if there is any
753 invalid input, otherwise it returns success without performing
754 any checkpointing. This can be used to check whether the ioctl exists on a
755 system and to verify there are no issues with arguments or flags. The
756 other two flags are EXT4_IOC_CHECKPOINT_FLAG_DISCARD and
757 EXT4_IOC_CHECKPOINT_FLAG_ZEROOUT. These flags cause the journal blocks to be
758 discarded or zero-filled, respectively, after the journal checkpoint is
759 complete. EXT4_IOC_CHECKPOINT_FLAG_DISCARD and EXT4_IOC_CHECKPOINT_FLAG_ZEROOUT
760 cannot both be set. The ioctl may be useful when snapshotting a system or for
761 complying with content deletion SLOs.
762

3. 한국어 전문 번역

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

JBD2 journal과 기록 mode

1-55

ext3에서 도입된 journal은 system crash 중 metadata update가 절반만 반영되어 파일시스템이 불일치 상태에 빠지는 것을 막습니다. 파일시스템 안에 최대 10,240,000개 block을 journal 공간으로 예약할 수 있으며 정확한 크기 제한은 `mke2fs(8)` 문서를 참조합니다.

중요한 transaction을 먼저 journal에 빠르게 기록하고 disk write cache까지 flush한 뒤 commit record를 씁니다. 이후 journal code가 transaction을 최종 disk 위치로 옮기고 commit record를 지웁니다. 느린 두 번째 기록 도중 crash가 나도 마지막 유효 commit까지 replay해 journal을 통과한 변경의 atomicity를 보장합니다.

기본 `data=ordered` mode는 filesystem metadata만 journal을 거칩니다. crash 뒤 file data block의 일관성은 보장하지 않지만, metadata를 commit하기 전에 관련 dirty data block을 먼저 disk에 flush합니다.

`data=journal`은 data와 metadata를 모두 journal을 거쳐 기록하므로 가장 안전하지만 느립니다. `data=writeback`은 journal metadata를 기록하기 전에 dirty data block을 flush하지 않습니다.

`data=ordered`에서는 commit latency를 크게 줄이는 fast commit도 지원합니다. 기존 full commit은 metadata block 전체를 JBD2 journal에 기록하지만 fast commit은 영향을 받은 metadata를 재구성하는 데 필요한 최소 delta만 JBD2와 공유하는 fast commit 영역에 저장합니다.

fast commit 영역이 차거나 fast commit이 불가능하거나 JBD2 commit timer가 만료되면 전통적인 full commit을 수행합니다. full commit은 앞선 fast commit을 모두 무효화해 영역을 다시 비웁니다. 이 기능은 `mkfs` 때 활성화해야 합니다.

journal inode는 보통 inode 8입니다. 처음 68바이트는 ext4 superblock에도 복제됩니다. journal 자체는 파일시스템 안의 일반적이지만 숨겨진 file이며 보통 block group 전체를 사용하고, `mke2fs`는 disk 중앙에 배치하려 합니다.

JBD2의 모든 on-disk field는 ext4와 반대인 big-endian으로 기록됩니다. ext4와 ocfs2가 모두 JBD2를 사용합니다. ext4 내부 journal의 최대 크기는 `2^32` block이지만 JBD2 자체에는 이 제한이 없는 것으로 보입니다.

ext4 journal mode
ModeJournal 대상Data flush 순서특징
`data=ordered`metadatametadata commit 전에 dirty data flush기본값, fast commit 지원
`data=journal`data + metadata모두 journal을 통해 기록가장 안전하지만 가장 느림
`data=writeback`metadatametadata journal 전에 dirty data flush 안 함순서 보장이 가장 약함

mount mode별 data와 metadata의 순서 및 보장 수준입니다.

journal transaction 수명
중요 data·metadata를 journal에 기록disk write cache까지 flushcommit record 기록transaction을 최종 disk 위치로 checkpointcommit record와 재사용 가능한 journal 공간 정리crash 시 마지막 유효 commit까지 replay

변경을 journal에 기록한 뒤 최종 위치로 옮기는 순서입니다.

.. SPDX-License-Identifier: GPL-2.0

Journal (jbd2)
--------------

Introduced in ext3, the ext4 filesystem employs a journal to protect the
filesystem against metadata inconsistencies in the case of a system crash. Up
to 10,240,000 file system blocks (see man mke2fs(8) for more details on journal
size limits) can be reserved inside the filesystem as a place to land
“important” data writes on-disk as quickly as possible. Once the important
data transaction is fully written to the disk and flushed from the disk write
cache, a record of the data being committed is also written to the journal. At
some later point in time, the journal code writes the transactions to their
final locations on disk (this could involve a lot of seeking or a lot of small
read-write-erases) before erasing the commit record. Should the system
crash during the second slow write, the journal can be replayed all the
way to the latest commit record, guaranteeing the atomicity of whatever
gets written through the journal to the disk. The effect of this is to
guarantee that the filesystem does not become stuck midway through a
metadata update.

For performance reasons, ext4 by default only writes filesystem metadata
through the journal. This means that file data blocks are /not/
guaranteed to be in any consistent state after a crash. If this default
guarantee level (``data=ordered``) is not satisfactory, there is a mount
option to control journal behavior. If ``data=journal``, all data and
metadata are written to disk through the journal. This is slower but
safest. If ``data=writeback``, dirty data blocks are not flushed to the
disk before the metadata are written to disk through the journal.

In case of ``data=ordered`` mode, Ext4 also supports fast commits which
help reduce commit latency significantly. The default ``data=ordered``
mode works by logging metadata blocks to the journal. In fast commit
mode, Ext4 only stores the minimal delta needed to recreate the
affected metadata in fast commit space that is shared with JBD2.
Once the fast commit area fills in or if fast commit is not possible
or if JBD2 commit timer goes off, Ext4 performs a traditional full commit.
A full commit invalidates all the fast commits that happened before
it and thus it makes the fast commit area empty for further fast
commits. This feature needs to be enabled at mkfs time.

The journal inode is typically inode 8. The first 68 bytes of the
journal inode are replicated in the ext4 superblock. The journal itself
is normal (but hidden) file within the filesystem. The file usually
consumes an entire block group, though mke2fs tries to put it in the
middle of the disk.

All fields in jbd2 are written to disk in big-endian order. This is the
opposite of ext4.

NOTE: Both ext4 and ocfs2 use jbd2.

The maximum size of a journal embedded in an ext4 filesystem is 2^32
blocks. jbd2 itself does not seem to care.

journal transaction 배치

56-77

journal은 superblock 뒤에 여러 transaction이 이어지는 형식입니다. 각 transaction은 descriptor block과 그에 대응하는 data block들로 시작하거나 revocation block으로 시작하고, 완료된 transaction은 항상 commit block으로 끝납니다.

commit record가 없거나 checksum이 맞지 않는 transaction은 recovery replay 중 폐기됩니다.

journal 선형 배치
Journal superblockDescriptor block 또는 revocation blockDescriptor가 설명하는 data block 또는 추가 revokeCommit block다음 transaction 반복

journal file 안에서 superblock과 transaction이 이어지는 순서입니다.

Layout
~~~~~~

Generally speaking, the journal has this format:

.. list-table::
   :widths: 16 48 16
   :header-rows: 1

   * - Superblock
     - descriptor_block (data_blocks or revocation_block) [more data or
       revocations] commmit_block
     - [more transactions...]
   * - 
     - One transaction
     -

Notice that a transaction begins with either a descriptor and some data,
or a block revocation list. A finished transaction always ends with a
commit. If there is no commit record (or the checksums don't match), the
transaction will be discarded during replay.

external journal device

78-104

ext4는 reserved inode를 쓰는 internal journal 대신 external journal device로 만들 수도 있습니다. 이 경우 filesystem device의 `s_journal_inum`은 0이고 `s_journal_uuid`가 설정되어야 합니다.

journal device에는 일반 위치에 ext4 superblock이 있고 UUID가 filesystem의 `s_journal_uuid`와 일치해야 합니다. journal superblock은 ext4 superblock 다음의 첫 full block에 있습니다.

external journal 배치
순서영역역할
11024 bytes paddingext4 superblock의 표준 위치 확보
2ext4 superblockjournal device 식별과 matching UUID
3journal superblock다음 full block에서 JBD2 log 설명
4descriptor/revoke + data한 transaction의 내용
5commit blocktransaction 완료 표시
6more transactions후속 transaction 반복

외부 journal device의 선형 on-disk 구성입니다.

External Journal
~~~~~~~~~~~~~~~~

Optionally, an ext4 filesystem can be created with an external journal
device (as opposed to an internal journal, which uses a reserved inode).
In this case, on the filesystem device, ``s_journal_inum`` should be
zero and ``s_journal_uuid`` should be set. On the journal device there
will be an ext4 super block in the usual place, with a matching UUID.
The journal superblock will be in the next full block after the
superblock.

.. list-table::
   :widths: 12 12 12 32 12
   :header-rows: 1

   * - 1024 bytes of padding
     - ext4 Superblock
     - Journal Superblock
     - descriptor_block (data_blocks or revocation_block) [more data or
       revocations] commmit_block
     - [more transactions...]
   * - 
     -
     -
     - One transaction
     -

공통 journal block header

105-156

journal의 모든 block은 12바이트 `struct journal_header_s`로 시작합니다. `h_magic`은 JBD2 magic `0xC03B3998`, `h_blocktype`은 block 종류, `h_sequence`는 연결된 transaction ID입니다.

block type 1은 뒤에 data block들이 오는 descriptor, 2는 transaction 완료를 나타내는 commit record, 3과 4는 journal superblock v1·v2, 5는 나중에 다시 쓰인 block을 recovery에서 건너뛰게 하는 revocation record입니다.

`struct journal_header_s`
OffsetTypeName설명
`0x0``__be32``h_magic`JBD2 magic `0xC03B3998`
`0x4``__be32``h_blocktype`journal block 종류
`0x8``__be32``h_sequence`transaction ID

모든 JBD2 block 앞의 12바이트 big-endian header입니다.

JBD2 block type
Value설명
`1`descriptor; 뒤의 data block 위치를 설명
`2`commit record; transaction 완료
`3`journal superblock v1
`4`journal superblock v2
`5`block revocation record

`h_blocktype`에 기록되는 값입니다.

Block Header
~~~~~~~~~~~~

Every block in the journal starts with a common 12-byte header
``struct journal_header_s``:

.. list-table::
   :widths: 8 8 24 40
   :header-rows: 1

   * - Offset
     - Type
     - Name
     - Description
   * - 0x0
     - __be32
     - h_magic
     - jbd2 magic number, 0xC03B3998.
   * - 0x4
     - __be32
     - h_blocktype
     - Description of what this block contains. See the jbd2_blocktype_ table
       below.
   * - 0x8
     - __be32
     - h_sequence
     - The transaction ID that goes with this block.

.. _jbd2_blocktype:

The journal block type can be any one of:

.. list-table::
   :widths: 16 64
   :header-rows: 1

   * - Value
     - Description
   * - 1
     - Descriptor. This block precedes a series of data blocks that were
       written through the journal during a transaction.
   * - 2
     - Block commit record. This block signifies the completion of a
       transaction.
   * - 3
     - Journal superblock, v1.
   * - 4
     - Journal superblock, v2.
   * - 5
     - Block revocation records. This speeds up recovery by enabling the
       journal to skip writing blocks that were subsequently rewritten.

journal superblock과 feature

157-344

journal superblock은 ext4 superblock보다 단순하며 journal 크기와 transaction log 시작 위치가 핵심입니다. `struct journal_superblock_s`는 1024바이트입니다.

앞부분은 journal의 정적 정보와 현재 log 상태를 담고, `0x24` 이후 대부분의 필드는 v2 superblock에서만 유효합니다.

`struct journal_superblock_s`
OffsetTypeName설명
`0x0``journal_header_t` 12 bytes`s_header`superblock임을 나타내는 공통 header
`0xC``__be32``s_blocksize`journal device block size
`0x10``__be32``s_maxlen`journal 전체 block 수
`0x14``__be32``s_first`log 정보의 첫 block
`0x18``__be32``s_sequence`log에서 기대하는 첫 commit ID
`0x1C``__be32``s_start`log 시작 block; 0이어도 clean을 뜻하지 않음
`0x20``__be32``s_errno``jbd2_journal_abort()`가 설정한 error
`0x24``__be32``s_feature_compat`compatible feature set
`0x28``__be32``s_feature_incompat`incompatible feature set
`0x2C``__be32``s_feature_ro_compat`read-only compatible set; 현재 없음
`0x30``__u8[16]``s_uuid`mount 시 ext4 superblock 사본과 비교할 journal UUID
`0x40``__be32``s_nr_users`journal을 공유하는 filesystem 수
`0x44``__be32``s_dynsuper`dynamic superblock copy 위치; 미사용 추정
`0x48``__be32``s_max_transaction`transaction당 journal block 한도; 미사용 추정
`0x4C``__be32``s_max_trans_data`transaction당 data block 한도; 미사용 추정
`0x50``__u8``s_checksum_type`journal checksum algorithm
`0x51``__u8[3]``s_padding2`padding
`0x54``__be32``s_num_fc_blocks`fast commit block 수
`0x58``__be32``s_head`journal이 비었을 때만 최신인 첫 unused block
`0x5C``__u32[40]``s_padding`padding
`0xFC``__be32``s_checksum`이 필드를 0으로 한 superblock 전체 checksum
`0x100``__u8[16*48]``s_users`log를 공유하는 filesystem ID 목록

1024바이트 JBD2 superblock의 필드를 offset 순서대로 정리합니다.

e2fsprogs와 Linux는 shared external journal을 허용하지 않지만 JBD2를 쓰는 Lustre나 ocfs2에서는 `s_users`를 사용할 가능성이 있습니다.

JBD2 compatible feature
ValueSymbol설명
`0x1``JBD2_FEATURE_COMPAT_CHECKSUM`data block checksum 유지

`s_feature_compat`의 현재 정의입니다.

JBD2 incompatible feature
ValueSymbol설명
`0x1``JBD2_FEATURE_INCOMPAT_REVOKE`block revocation record 지원
`0x2``JBD2_FEATURE_INCOMPAT_64BIT`64-bit block number 지원
`0x4``JBD2_FEATURE_INCOMPAT_ASYNC_COMMIT`asynchronous commit
`0x8``JBD2_FEATURE_INCOMPAT_CSUM_V2`metadata block별 checksum과 data tag checksum
`0x10``JBD2_FEATURE_INCOMPAT_CSUM_V3`v2와 같지만 block number 크기와 무관한 고정 tag 크기
`0x20``JBD2_FEATURE_INCOMPAT_FAST_COMMIT`fast commit block 지원

`s_feature_incompat`의 정의입니다.

checksum type은 보통 CRC32 또는 CRC32C를 사용합니다.

JBD2 checksum type
ValueAlgorithm
`1`CRC32
`2`MD5
`3`SHA1
`4`CRC32C

`s_checksum_type`과 commit header에 쓰는 code입니다.

Super Block
~~~~~~~~~~~

The super block for the journal is much simpler as compared to ext4's.
The key data kept within are size of the journal, and where to find the
start of the log of transactions.

The journal superblock is recorded as ``struct journal_superblock_s``,
which is 1024 bytes long:

.. list-table::
   :widths: 8 8 24 40
   :header-rows: 1

   * - Offset
     - Type
     - Name
     - Description
   * -
     -
     -
     - Static information describing the journal.
   * - 0x0
     - journal_header_t (12 bytes)
     - s_header
     - Common header identifying this as a superblock.
   * - 0xC
     - __be32
     - s_blocksize
     - Journal device block size.
   * - 0x10
     - __be32
     - s_maxlen
     - Total number of blocks in this journal.
   * - 0x14
     - __be32
     - s_first
     - First block of log information.
   * -
     -
     -
     - Dynamic information describing the current state of the log.
   * - 0x18
     - __be32
     - s_sequence
     - First commit ID expected in log.
   * - 0x1C
     - __be32
     - s_start
     - Block number of the start of log. Contrary to the comments, this field
       being zero does not imply that the journal is clean!
   * - 0x20
     - __be32
     - s_errno
     - Error value, as set by jbd2_journal_abort().
   * -
     -
     -
     - The remaining fields are only valid in a v2 superblock.
   * - 0x24
     - __be32
     - s_feature_compat;
     - Compatible feature set. See the table jbd2_compat_ below.
   * - 0x28
     - __be32
     - s_feature_incompat
     - Incompatible feature set. See the table jbd2_incompat_ below.
   * - 0x2C
     - __be32
     - s_feature_ro_compat
     - Read-only compatible feature set. There aren't any of these currently.
   * - 0x30
     - __u8
     - s_uuid[16]
     - 128-bit uuid for journal. This is compared against the copy in the ext4
       super block at mount time.
   * - 0x40
     - __be32
     - s_nr_users
     - Number of file systems sharing this journal.
   * - 0x44
     - __be32
     - s_dynsuper
     - Location of dynamic super block copy. (Not used?)
   * - 0x48
     - __be32
     - s_max_transaction
     - Limit of journal blocks per transaction. (Not used?)
   * - 0x4C
     - __be32
     - s_max_trans_data
     - Limit of data blocks per transaction. (Not used?)
   * - 0x50
     - __u8
     - s_checksum_type
     - Checksum algorithm used for the journal.  See jbd2_checksum_type_ for
       more info.
   * - 0x51
     - __u8[3]
     - s_padding2
     -
   * - 0x54
     - __be32
     - s_num_fc_blocks
     - Number of fast commit blocks in the journal.
   * - 0x58
     - __be32
     - s_head
     - Block number of the head (first unused block) of the journal, only
       up-to-date when the journal is empty.
   * - 0x5C
     - __u32
     - s_padding[40]
     -
   * - 0xFC
     - __be32
     - s_checksum
     - Checksum of the entire superblock, with this field set to zero.
   * - 0x100
     - __u8
     - s_users[16*48]
     - ids of all file systems sharing the log. e2fsprogs/Linux don't allow
       shared external journals, but I imagine Lustre (or ocfs2?), which use
       the jbd2 code, might.

.. _jbd2_compat:

The journal compat features are any combination of the following:

.. list-table::
   :widths: 16 64
   :header-rows: 1

   * - Value
     - Description
   * - 0x1
     - Journal maintains checksums on the data blocks.
       (JBD2_FEATURE_COMPAT_CHECKSUM)

.. _jbd2_incompat:

The journal incompat features are any combination of the following:

.. list-table::
   :widths: 16 64
   :header-rows: 1

   * - Value
     - Description
   * - 0x1
     - Journal has block revocation records. (JBD2_FEATURE_INCOMPAT_REVOKE)
   * - 0x2
     - Journal can deal with 64-bit block numbers.
       (JBD2_FEATURE_INCOMPAT_64BIT)
   * - 0x4
     - Journal commits asynchronously. (JBD2_FEATURE_INCOMPAT_ASYNC_COMMIT)
   * - 0x8
     - This journal uses v2 of the checksum on-disk format. Each journal
       metadata block gets its own checksum, and the block tags in the
       descriptor table contain checksums for each of the data blocks in the
       journal. (JBD2_FEATURE_INCOMPAT_CSUM_V2)
   * - 0x10
     - This journal uses v3 of the checksum on-disk format. This is the same as
       v2, but the journal block tag size is fixed regardless of the size of
       block numbers. (JBD2_FEATURE_INCOMPAT_CSUM_V3)
   * - 0x20
     - Journal has fast commit blocks. (JBD2_FEATURE_INCOMPAT_FAST_COMMIT)

.. _jbd2_checksum_type:

Journal checksum type codes are one of the following.  crc32 or crc32c are the
most likely choices.

.. list-table::
   :widths: 16 64
   :header-rows: 1

   * - Value
     - Description
   * - 1
     - CRC32
   * - 2
     - MD5
   * - 3
     - SHA1
   * - 4
     - CRC32C

descriptor block과 block tag

345-508

descriptor block은 journal에서 바로 뒤따르는 data block들이 최종적으로 기록될 disk 위치를 설명하는 journal block tag 배열입니다. 완전히 독립된 structure가 아니라 open-coded 형식이며 최소 36바이트를 소비하지만 full block을 사용합니다.

descriptor block
OffsetTypeName설명
`0x0``journal_header_t`open coded공통 block header
`0xC``struct journal_block_tag_s[]`open coded arrayblock을 채우거나 뒤의 모든 data block을 설명할 만큼의 tag

공통 header 뒤에 block tag 배열이 이어집니다.

`JBD2_FEATURE_INCOMPAT_CSUM_V3`이면 16바이트 또는 UUID를 포함한 32바이트 `struct journal_block_tag3_s`를 사용합니다. `t_blocknr`와 `t_blocknr_high`를 결합해 최종 disk 위치를 나타내고, 64-bit 기능이 없으면 high field는 0입니다.

v3의 `t_checksum`은 journal UUID, sequence number, data block의 checksum입니다. same UUID flag가 없으면 tag 끝에 16바이트 UUID가 open-coded로 붙습니다.

`struct journal_block_tag3_s`
OffsetTypeName설명
`0x0``__be32``t_blocknr`최종 disk block number 하위 32비트
`0x4``__be32``t_flags`descriptor tag flag
`0x8``__be32``t_blocknr_high`최종 block number 상위 32비트; 64BIT 아니면 0
`0xC``__be32``t_checksum`UUID + sequence + data block checksum
`0x10``char[16]``uuid`same UUID flag가 없을 때만 tag 끝에 추가

CSUM_V3의 고정 core tag와 선택적 UUID입니다.

journal tag flag
Value설명
`0x1`escaped; data 첫 4바이트가 JBD2 magic과 같아 0으로 치환됨
`0x2`이전 tag와 UUID가 같아 UUID field 생략
`0x4`transaction에서 data block 삭제; 미사용 추정
`0x8`descriptor block의 마지막 tag

`t_flags`의 조합입니다.

CSUM_V3가 아니면 크기가 8, 12, 24, 또는 28바이트인 `struct journal_block_tag_s`를 사용합니다. `t_checksum`은 같은 입력 checksum의 하위 16비트만 저장합니다. 64-bit block number 지원 시 `t_blocknr_high`를 추가하고, same UUID flag가 없으면 마지막에 UUID를 붙입니다.

`struct journal_block_tag_s`
OffsetTypeName설명
`0x0``__be32``t_blocknr`최종 disk block number 하위 32비트
`0x4``__be16``t_checksum`UUID + sequence + data block checksum 하위 16비트
`0x6``__be16``t_flags`descriptor tag flag
`0x8``__be32``t_blocknr_high`64-bit block number feature일 때만 존재
`0x8` 또는 `0xC``char[16]``uuid`same UUID flag가 없을 때 tag 끝에 추가

CSUM_V3가 아닐 때 feature와 flag에 따라 길이가 달라지는 tag입니다.

CSUM_V2 또는 CSUM_V3이면 descriptor block 끝에 `struct jbd2_journal_block_tail`이 있습니다. 4바이트 `t_checksum`은 field를 0으로 둔 journal UUID + descriptor block의 checksum입니다.

`struct jbd2_journal_block_tail`
OffsetTypeName설명
`0x0``__be32``t_checksum`UUID + zeroed-tail descriptor block checksum

descriptor metadata block 자체를 보호합니다.

Descriptor Block
~~~~~~~~~~~~~~~~

The descriptor block contains an array of journal block tags that
describe the final locations of the data blocks that follow in the
journal. Descriptor blocks are open-coded instead of being completely
described by a data structure, but here is the block structure anyway.
Descriptor blocks consume at least 36 bytes, but use a full block:

.. list-table::
   :widths: 8 8 24 40
   :header-rows: 1

   * - Offset
     - Type
     - Name
     - Descriptor
   * - 0x0
     - journal_header_t
     - (open coded)
     - Common block header.
   * - 0xC
     - struct journal_block_tag_s
     - open coded array[]
     - Enough tags either to fill up the block or to describe all the data
       blocks that follow this descriptor block.

Journal block tags have any of the following formats, depending on which
journal feature and block tag flags are set.

If JBD2_FEATURE_INCOMPAT_CSUM_V3 is set, the journal block tag is
defined as ``struct journal_block_tag3_s``, which looks like the
following. The size is 16 or 32 bytes.

.. list-table::
   :widths: 8 8 24 40
   :header-rows: 1

   * - Offset
     - Type
     - Name
     - Descriptor
   * - 0x0
     - __be32
     - t_blocknr
     - Lower 32-bits of the location of where the corresponding data block
       should end up on disk.
   * - 0x4
     - __be32
     - t_flags
     - Flags that go with the descriptor. See the table jbd2_tag_flags_ for
       more info.
   * - 0x8
     - __be32
     - t_blocknr_high
     - Upper 32-bits of the location of where the corresponding data block
       should end up on disk. This is zero if JBD2_FEATURE_INCOMPAT_64BIT is
       not enabled.
   * - 0xC
     - __be32
     - t_checksum
     - Checksum of the journal UUID, the sequence number, and the data block.
   * -
     -
     -
     - This field appears to be open coded. It always comes at the end of the
       tag, after t_checksum. This field is not present if the "same UUID" flag
       is set.
   * - 0x8 or 0xC
     - char
     - uuid[16]
     - A UUID to go with this tag. This field appears to be copied from the
       ``j_uuid`` field in ``struct journal_s``, but only tune2fs touches that
       field.

.. _jbd2_tag_flags:

The journal tag flags are any combination of the following:

.. list-table::
   :widths: 16 64
   :header-rows: 1

   * - Value
     - Description
   * - 0x1
     - On-disk block is escaped. The first four bytes of the data block just
       happened to match the jbd2 magic number.
   * - 0x2
     - This block has the same UUID as previous, therefore the UUID field is
       omitted.
   * - 0x4
     - The data block was deleted by the transaction. (Not used?)
   * - 0x8
     - This is the last tag in this descriptor block.

If JBD2_FEATURE_INCOMPAT_CSUM_V3 is NOT set, the journal block tag
is defined as ``struct journal_block_tag_s``, which looks like the
following. The size is 8, 12, 24, or 28 bytes:

.. list-table::
   :widths: 8 8 24 40
   :header-rows: 1

   * - Offset
     - Type
     - Name
     - Descriptor
   * - 0x0
     - __be32
     - t_blocknr
     - Lower 32-bits of the location of where the corresponding data block
       should end up on disk.
   * - 0x4
     - __be16
     - t_checksum
     - Checksum of the journal UUID, the sequence number, and the data block.
       Note that only the lower 16 bits are stored.
   * - 0x6
     - __be16
     - t_flags
     - Flags that go with the descriptor. See the table jbd2_tag_flags_ for
       more info.
   * -
     -
     -
     - This next field is only present if the super block indicates support for
       64-bit block numbers.
   * - 0x8
     - __be32
     - t_blocknr_high
     - Upper 32-bits of the location of where the corresponding data block
       should end up on disk.
   * -
     -
     -
     - This field appears to be open coded. It always comes at the end of the
       tag, after t_flags or t_blocknr_high. This field is not present if the
       "same UUID" flag is set.
   * - 0x8 or 0xC
     - char
     - uuid[16]
     - A UUID to go with this tag. This field appears to be copied from the
       ``j_uuid`` field in ``struct journal_s``, but only tune2fs touches that
       field.

If JBD2_FEATURE_INCOMPAT_CSUM_V2 or
JBD2_FEATURE_INCOMPAT_CSUM_V3 are set, the end of the block is a
``struct jbd2_journal_block_tail``, which looks like this:

.. list-table::
   :widths: 8 8 24 40
   :header-rows: 1

   * - Offset
     - Type
     - Name
     - Descriptor
   * - 0x0
     - __be32
     - t_checksum
     - Checksum of the journal UUID + the descriptor block, with this field set
       to zero.

journal data block escape

509-517

journal을 통해 disk에 쓸 data block은 일반적으로 descriptor block 뒤의 journal file에 그대로 기록합니다.

data block의 처음 4바이트가 우연히 JBD2 magic number와 같으면 그 4바이트를 0으로 바꾸고 descriptor tag에 escaped flag `0x1`을 설정합니다. replay는 flag를 보고 원래 magic을 복원합니다.

data block escape
data block 첫 4 bytes 확인JBD2 magic과 다르면 그대로 journal에 기록magic과 같으면 첫 4 bytes를 0으로 치환descriptor tag에 escaped flag 설정replay 때 magic bytes 복원

journal metadata로 오인될 수 있는 data prefix를 안전하게 저장하는 과정입니다.

Data Block
~~~~~~~~~~

In general, the data blocks being written to disk through the journal
are written verbatim into the journal file after the descriptor block.
However, if the first four bytes of the block match the jbd2 magic
number then those four bytes are replaced with zeroes and the “escaped”
flag is set in the descriptor block tag.

revocation block

518-579

revocation block은 이전 transaction에 있던 block을 replay하지 못하게 합니다. 한때 journal 대상이었던 metadata block이 해제된 뒤 file data block으로 재할당된 경우, 오래된 journal 내용을 다시 쓰면 새 file data가 손상되므로 해당 block을 revoke합니다.

이 장치는 한 journal block이 다른 journal block으로 대체되었다는 뜻이 아닙니다. block을 새 transaction에 추가하면 그 block에 대한 기존 revocation record를 모두 제거합니다.

`struct jbd2_journal_revoke_header_s`는 최소 16바이트지만 full block을 사용합니다. `r_count` 뒤에는 이 transaction이 revoke하는 block number의 선형 배열이 옵니다. superblock이 64-bit block number를 지원하면 각 번호는 8바이트, 아니면 4바이트입니다.

`struct jbd2_journal_revoke_header_s`
OffsetTypeName설명
`0x0``journal_header_t``r_header`공통 block header
`0xC``__be32``r_count`이 block에서 사용한 byte 수
`0x10``__be32` 또는 `__be64``blocks[0]`revoke할 block number 배열

revoke 대상 block 배열의 header입니다.

CSUM_V2 또는 CSUM_V3이면 revocation block 끝의 `struct jbd2_journal_revoke_tail`에 journal UUID + revocation block checksum을 저장합니다.

`struct jbd2_journal_revoke_tail`
OffsetTypeName설명
`0x0``__be32``r_checksum`journal UUID + revocation block checksum

revocation metadata block의 checksum tail입니다.

Revocation Block
~~~~~~~~~~~~~~~~

A revocation block is used to prevent replay of a block in an earlier
transaction. This is used to mark blocks that were journalled at one
time but are no longer journalled. Typically this happens if a metadata
block is freed and re-allocated as a file data block; in this case, a
journal replay after the file block was written to disk will cause
corruption.

**NOTE**: This mechanism is NOT used to express “this journal block is
superseded by this other journal block”, as the author (djwong)
mistakenly thought. Any block being added to a transaction will cause
the removal of all existing revocation records for that block.

Revocation blocks are described in
``struct jbd2_journal_revoke_header_s``, are at least 16 bytes in
length, but use a full block:

.. list-table::
   :widths: 8 8 24 40
   :header-rows: 1

   * - Offset
     - Type
     - Name
     - Description
   * - 0x0
     - journal_header_t
     - r_header
     - Common block header.
   * - 0xC
     - __be32
     - r_count
     - Number of bytes used in this block.
   * - 0x10
     - __be32 or __be64
     - blocks[0]
     - Blocks to revoke.

After r_count is a linear array of block numbers that are effectively
revoked by this transaction. The size of each block number is 8 bytes if
the superblock advertises 64-bit block number support, or 4 bytes
otherwise.

If JBD2_FEATURE_INCOMPAT_CSUM_V2 or
JBD2_FEATURE_INCOMPAT_CSUM_V3 are set, the end of the revocation
block is a ``struct jbd2_journal_revoke_tail``, which has this format:

.. list-table::
   :widths: 8 8 24 40
   :header-rows: 1

   * - Offset
     - Type
     - Name
     - Description
   * - 0x0
     - __be32
     - r_checksum
     - Checksum of the journal UUID + revocation block

commit block

580-633

commit block은 transaction 전체가 journal에 기록되었음을 나타내는 sentry입니다. 이 block이 journal에 도달한 뒤 transaction data를 최종 disk 위치에 쓸 수 있습니다.

`struct commit_header`는 32바이트로 설명되지만 full block을 사용합니다. 공통 header 뒤에 checksum type과 size, checksum 공간, commit timestamp가 옵니다.

CSUM_V2 또는 CSUM_V3이면 `h_chksum`의 첫 `__be32`는 해당 field를 0으로 한 journal UUID + commit block 전체 checksum입니다. COMPAT_CHECKSUM이면 transaction에 이미 쓴 모든 block의 CRC32입니다.

`struct commit_header`
OffsetTypeName설명
`0x0``journal_header_s`open coded공통 block header
`0xC``unsigned char``h_chksum_type`transaction data checksum type
`0xD``unsigned char``h_chksum_size`checksum byte 수; 보통 4
`0xE``unsigned char[2]``h_padding`padding
`0x10``__be32[]` 32 bytes`h_chksum[JBD2_CHECKSUM_BYTES]`commit 또는 transaction checksum 공간
`0x30``__be64``h_commit_sec`epoch 이후 commit seconds
`0x38``__be32``h_commit_nsec`commit timestamp nanoseconds

transaction 완료와 무결성·시각을 기록하는 commit block입니다.

Commit Block
~~~~~~~~~~~~

The commit block is a sentry that indicates that a transaction has been
completely written to the journal. Once this commit block reaches the
journal, the data stored with this transaction can be written to their
final locations on disk.

The commit block is described by ``struct commit_header``, which is 32
bytes long (but uses a full block):

.. list-table::
   :widths: 8 8 24 40
   :header-rows: 1

   * - Offset
     - Type
     - Name
     - Descriptor
   * - 0x0
     - journal_header_s
     - (open coded)
     - Common block header.
   * - 0xC
     - unsigned char
     - h_chksum_type
     - The type of checksum to use to verify the integrity of the data blocks
       in the transaction. See jbd2_checksum_type_ for more info.
   * - 0xD
     - unsigned char
     - h_chksum_size
     - The number of bytes used by the checksum. Most likely 4.
   * - 0xE
     - unsigned char
     - h_padding[2]
     -
   * - 0x10
     - __be32
     - h_chksum[JBD2_CHECKSUM_BYTES]
     - 32 bytes of space to store checksums. If
       JBD2_FEATURE_INCOMPAT_CSUM_V2 or JBD2_FEATURE_INCOMPAT_CSUM_V3
       are set, the first ``__be32`` is the checksum of the journal UUID and
       the entire commit block, with this field zeroed. If
       JBD2_FEATURE_COMPAT_CHECKSUM is set, the first ``__be32`` is the
       crc32 of all the blocks already written to the transaction.
   * - 0x30
     - __be64
     - h_commit_sec
     - The time that the transaction was committed, in seconds since the epoch.
   * - 0x38
     - __be32
     - h_commit_nsec
     - Nanoseconds component of the above timestamp.

fast commit TLV

634-688

fast commit 영역은 tag-length-value log로 구성됩니다. 각 TLV는 tag와 전체 field 길이를 저장하는 `struct ext4_fc_tl`로 시작하고, 뒤에 tag별 variable-length value가 옵니다.

fast commit tag
Tag의미Value struct저장 내용
`EXT4_FC_TAG_HEAD`fast commit area header`struct ext4_fc_head`이 fast commit들을 적용해야 하는 선행 transaction TID
`EXT4_FC_TAG_ADD_RANGE`inode에 extent 추가`struct ext4_fc_add_range`inode number와 추가할 extent
`EXT4_FC_TAG_DEL_RANGE`inode logical offset 제거`struct ext4_fc_del_range`inode number와 제거할 logical range
`EXT4_FC_TAG_CREAT`새 파일 directory entry 생성`struct ext4_fc_dentry_info`parent inode, inode number, 새 directory entry
`EXT4_FC_TAG_LINK`directory entry를 inode에 link`struct ext4_fc_dentry_info`parent inode, inode number, directory entry
`EXT4_FC_TAG_UNLINK`inode의 directory entry unlink`struct ext4_fc_dentry_info`parent inode, inode number, directory entry
`EXT4_FC_TAG_PAD`paddingNonefast commit 영역의 unused bytes
`EXT4_FC_TAG_TAIL`fast commit 끝 표시`struct ext4_fc_tail`commit TID와 해당 fast commit CRC

지원되는 TLV tag와 재구성 결과입니다.

Fast commits
~~~~~~~~~~~~

Fast commit area is organized as a log of tag length values. Each TLV has
a ``struct ext4_fc_tl`` in the beginning which stores the tag and the length
of the entire field. It is followed by variable length tag specific value.
Here is the list of supported tags and their meanings:

.. list-table::
   :widths: 8 20 20 32
   :header-rows: 1

   * - Tag
     - Meaning
     - Value struct
     - Description
   * - EXT4_FC_TAG_HEAD
     - Fast commit area header
     - ``struct ext4_fc_head``
     - Stores the TID of the transaction after which these fast commits should
       be applied.
   * - EXT4_FC_TAG_ADD_RANGE
     - Add extent to inode
     - ``struct ext4_fc_add_range``
     - Stores the inode number and extent to be added in this inode
   * - EXT4_FC_TAG_DEL_RANGE
     - Remove logical offsets to inode
     - ``struct ext4_fc_del_range``
     - Stores the inode number and the logical offset range that needs to be
       removed
   * - EXT4_FC_TAG_CREAT
     - Create directory entry for a newly created file
     - ``struct ext4_fc_dentry_info``
     - Stores the parent inode number, inode number and directory entry of the
       newly created file
   * - EXT4_FC_TAG_LINK
     - Link a directory entry to an inode
     - ``struct ext4_fc_dentry_info``
     - Stores the parent inode number, inode number and directory entry
   * - EXT4_FC_TAG_UNLINK
     - Unlink a directory entry of an inode
     - ``struct ext4_fc_dentry_info``
     - Stores the parent inode number, inode number and directory entry

   * - EXT4_FC_TAG_PAD
     - Padding (unused area)
     - None
     - Unused bytes in the fast commit area.

   * - EXT4_FC_TAG_TAIL
     - Mark the end of a fast commit
     - ``struct ext4_fc_tail``
     - Stores the TID of the commit, CRC of the fast commit of which this tag
       represents the end of

fast commit replay의 멱등성

689-739

recovery code가 정해진 규칙을 따르면 fast commit tag는 본질적으로 idempotent합니다. commit path의 원칙은 작업 절차가 아니라 작업 결과를 저장하는 것입니다.

예를 들어 inode 10에 연결된 `/a`를 `/b`로 rename하는 `mv /a /b`는 'a를 b로 rename'이라는 절차 대신 `b를 inode 10에 link`, `a를 unlink`, `inode 10의 유효한 refcount`라는 결과의 연속으로 기록합니다. recovery는 이 최종 상태를 filesystem에 강제합니다.

`rm A`, `mv B A`, `read A`라는 절차를 그대로 저장하면 두 번째 단계 뒤 crash한 다음 replay에서 새로 만들어진 A를 다시 지워 non-idempotent해집니다.

fast commit은 replay 전 A가 inode 10, B가 inode 11이었다고 할 때 `Unlink A`, `Link A to inode 11`, `Unlink B`, `Inode 11`로 기록합니다. 세 번째 결과 뒤 crash해도 다음 replay가 A를 잠시 제거했다 다시 inode 11에 연결하고, 없는 B는 건너뛰며 마지막 inode tag로 refcount를 고칩니다.

즉 non-idempotent procedure를 idempotent outcome의 연속으로 바꾸어 replay를 여러 번 수행해도 같은 최종 상태를 얻습니다.

fast commit 멱등 replay
operation의 최종 directory·inode 상태 계산link/unlink/extent/inode 결과를 TLV로 기록recovery에서 각 결과를 filesystem에 강제이미 없는 entry는 안전하게 건너뜀마지막 inode tag로 refcount와 metadata 확정replay 반복 시 같은 최종 상태로 수렴

절차 대신 결과를 기록해 crash가 반복돼도 상태를 수렴시키는 방식입니다.

Fast Commit Replay Idempotence
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Fast commits tags are idempotent in nature provided the recovery code follows
certain rules. The guiding principle that the commit path follows while
committing is that it stores the result of a particular operation instead of
storing the procedure.

Let's consider this rename operation: 'mv /a /b'. Let's assume dirent '/a'
was associated with inode 10. During fast commit, instead of storing this
operation as a procedure "rename a to b", we store the resulting file system
state as a "series" of outcomes:

- Link dirent b to inode 10
- Unlink dirent a
- Inode 10 with valid refcount

Now when recovery code runs, it needs "enforce" this state on the file
system. This is what guarantees idempotence of fast commit replay.

Let's take an example of a procedure that is not idempotent and see how fast
commits make it idempotent. Consider following sequence of operations:

1) rm A
2) mv B A
3) read A

If we store this sequence of operations as is then the replay is not idempotent.
Let's say while in replay, we crash after (2). During the second replay,
file A (which was actually created as a result of "mv B A" operation) would get
deleted. Thus, file named A would be absent when we try to read A. So, this
sequence of operations is not idempotent. However, as mentioned above, instead
of storing the procedure fast commits store the outcome of each procedure. Thus
the fast commit log for above procedure would be as follows:

(Let's assume dirent A was linked to inode 10 and dirent B was linked to
inode 11 before the replay)

1) Unlink A
2) Link A to inode 11
3) Unlink B
4) Inode 11

If we crash after (3) we will have file A linked to inode 11. During the second
replay, we will remove file A (inode 11). But we will create it back and make
it point to inode 11. We won't find B, so we'll just skip that step. At this
point, the refcount for inode 11 is not reliable, but that gets fixed by the
replay of last inode 11 tag. Thus, by converting a non-idempotent procedure
into a series of idempotent outcomes, fast commits ensured idempotence during
the replay.

journal checkpoint ioctl

740-761

journal checkpoint는 모든 transaction과 연결된 buffer를 disk에 제출하도록 보장합니다. 진행 중인 transaction도 완료를 기다려 checkpoint에 포함합니다.

journal recovery, filesystem resize, `journal_t` structure 해제 같은 중요한 filesystem update에서 내부적으로 사용합니다.

userspace에서는 `EXT4_IOC_CHECKPOINT` ioctl로 checkpoint를 시작할 수 있습니다. 인자는 flag를 담은 단일 `u64`입니다.

`EXT4_IOC_CHECKPOINT_FLAG_DRY_RUN`은 ioctl 입력을 검증합니다. 잘못된 입력이면 error를 반환하고, 유효하면 실제 checkpoint 없이 성공하므로 ioctl 존재 여부와 argument·flag 문제를 확인할 수 있습니다.

`EXT4_IOC_CHECKPOINT_FLAG_DISCARD`와 `EXT4_IOC_CHECKPOINT_FLAG_ZEROOUT`은 checkpoint 완료 뒤 journal block을 각각 discard하거나 0으로 채웁니다. 두 flag는 동시에 설정할 수 없습니다.

이 ioctl은 system snapshot을 만들거나 content deletion SLO를 준수할 때 유용할 수 있습니다.

`EXT4_IOC_CHECKPOINT` flag
Flag동작제약
`EXT4_IOC_CHECKPOINT_FLAG_DRY_RUN`입력만 검증하고 checkpoint하지 않음invalid input은 error
`EXT4_IOC_CHECKPOINT_FLAG_DISCARD`완료 뒤 journal block discardZEROOUT과 동시 사용 불가
`EXT4_IOC_CHECKPOINT_FLAG_ZEROOUT`완료 뒤 journal block을 0으로 채움DISCARD와 동시 사용 불가

userspace checkpoint 동작을 제어하는 세 flag입니다.

Journal Checkpoint
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Checkpointing the journal ensures all transactions and their associated buffers
are submitted to the disk. In-progress transactions are waited upon and included
in the checkpoint. Checkpointing is used internally during critical updates to
the filesystem including journal recovery, filesystem resizing, and freeing of
the journal_t structure.

A journal checkpoint can be triggered from userspace via the ioctl
EXT4_IOC_CHECKPOINT. This ioctl takes a single, u64 argument for flags.
Currently, three flags are supported. First, EXT4_IOC_CHECKPOINT_FLAG_DRY_RUN
can be used to verify input to the ioctl. It returns error if there is any
invalid input, otherwise it returns success without performing
any checkpointing. This can be used to check whether the ioctl exists on a
system and to verify there are no issues with arguments or flags. The
other two flags are EXT4_IOC_CHECKPOINT_FLAG_DISCARD and
EXT4_IOC_CHECKPOINT_FLAG_ZEROOUT. These flags cause the journal blocks to be
discarded or zero-filled, respectively, after the journal checkpoint is
complete. EXT4_IOC_CHECKPOINT_FLAG_DISCARD and EXT4_IOC_CHECKPOINT_FLAG_ZEROOUT
cannot both be set. The ioctl may be useful when snapshotting a system or for
complying with content deletion SLOs.