はじめに
ext4についての資料はググればまだ探せばある方だけど、ことjournalについては、mountオプションと概要くらいしかなくて、つっこんだことまで説明した資料が皆無な状態。というわけで、詳細を追うことにした。無茶なことしやがって(AA略
なお、Linux-4.6くらいをみてます。
Documentation
概要やらmountオプションやらについては、下記のぐたぐたした文章を読むよりもkernel/Documentation/filesystems/ext4.txtのドキュメントを参照する方がよい。
jdb2
ext4(またはext3)のjournalは正確にはext4の一部ではなくて**jbd2(Journaling block device 2)**というモジュールとして汎用化されている。ただ実体は、ext4はまだ他のjournalに載せ替えれそうな設計になっているが、jdb2はもうext4専用といわざるをえないほど密結合しているように読めた。
ext4としては多くの場合journalはinode(つまり普通のファイル)としてストレージに保存する。kernel/fs/ext4.hより、
209 /*
210 * Special inodes numbers
211 */
212 #define EXT4_BAD_INO 1 /* Bad blocks inode */
213 #define EXT4_ROOT_INO 2 /* Root inode */
214 #define EXT4_USR_QUOTA_INO 3 /* User quota inode */
215 #define EXT4_GRP_QUOTA_INO 4 /* Group quota inode */
216 #define EXT4_BOOT_LOADER_INO 5 /* Boot loader inode */
217 #define EXT4_UNDEL_DIR_INO 6 /* Undelete directory inode */
218 #define EXT4_RESIZE_INO 7 /* Reserved group descriptors inode */
219 #define EXT4_JOURNAL_INO 8 /* Journal inode */
となっていて、通常はinode番号8が使われる。が、コードとしてはsuerblockにあるs_journal_inumのものが使われるので、古いe2fsprogsを使った、過去から引きついだなどの特殊な場合はこの限りでない模様。で、このinode番号のものはmount/umount時に特殊に扱われ、journalの読み書きに使われる。
journalファイルは通常e2fsprogsの**mke2fs(mkfs.ext4)**で作られる。lib/ext2fs/mkjournal.c:ext2fs_add_journal_inode2()より、
} else {
if ((mount_flags & EXT2_MF_BUSY) &&
!(fs->flags & EXT2_FLAG_EXCLUSIVE)) {
retval = EBUSY;
goto errout;
}
journal_ino = EXT2_JOURNAL_INO;
if ((retval = write_journal_inode(fs, journal_ino,
num_blocks, goal, flags)))
return retval;
}
mountされている状態から作ろうとすると、EXT2_IOC_SETFLAGSを使って**UF_NODUMP|UF_IMMUTABLE(immutableかつno dump)**フラグを立てるが、mountされていなかったら上記の通り特になにもしていない。lookupできない(名前からたどれない)inodeだから特にIMMUTALBEにしなくてもよいということなんだろうか。
# debugfs /dev/sa1
debugfs 1.42.13 (17-May-2015)
debugfs: stat <8>
Inode: 8 Type: regular Mode: 0600 Flags: 0x80000
Generation: 0 Version: 0x00000000:00000000
User: 0 Group: 0 Size: 134217728
File ACL: 0 Directory ACL: 0
Links: 1 Blockcount: 262144
Fragment: Address: 0 Number: 0 Size: 0
ctime: 0x5878dec7:00000000 -- Fri Jan 13 23:05:59 2017
atime: 0x5878dec7:00000000 -- Fri Jan 13 23:05:59 2017
mtime: 0x5878dec7:00000000 -- Fri Jan 13 23:05:59 2017
crtime: 0x5878dec7:00000000 -- Fri Jan 13 23:05:59 2017
Size of extra inode fields: 28
EXTENTS:
(0-32766):2655233-2687999, (32767):2688000
手元でinode番号8を確認すると、flags: 0x80000 なので、EXT4_EXTENTS_FLのみのようだった。
ちなみに、外部journalもサポートしていて、mkfs.ext4 -J device=/dev/sda1で作る。ただ、ページにも書いてあるとおり、外部ブロックデバイス名がいつも同じになるとは限らなかったりするので、オペレーションに手間を掛けたくないなら使わない方がよい。またjournal_async_commitでないとダメと書かれていてさらに不安になる。どうダメなのか裏付けしたかったけど、kernelソース的には特にそれっぽいのは見あたらなかった。
journalモード
3種類のモードがある。default mount option(HOGE_DEFM_HOGEのやつ)、もしくはmountオプションに指定する。指定がない場合data=orderedになる。
-
data=writeback、metadata(ファイルサイズなど)とデータの中身の書き出し順が保障されない。セキュリティ面でもよくないとドキュメントに書いてあり、よほどパフォーマンスを気にする場面以外ではもう使わないんじゃないかと思う。
そのパフォーマンスも、手元でテストしてみた限りdata=orderedと有意な差がなかった。data=writebackだと差がわかる程度には少し早かった。ただdata=journalはさすがに遅かった。 - data=ordered、metaデータとデータの中身の書き出し順が保障される。標準のモード。
- data=journal、データの中身も一度journalに書き出してから実際の書き込みが行われる。もうjournalというよりtransactionといった方がいいかもしれない。
ただ、ext4が増築で機能が増えてきた経緯もあってか、journalモードによって使える機能に制限がかかることが多い。つまみ食い程度に下記に具体例を。
kernel/fs/ext4/super.c:ext4_fill_super()より、data=journalだとDELALLOCは無効になる。さかのぼる限り、dd919b982、つまりDEALLOC当初からの模様。実装が難しくなる(他のjournalモードとは別のpathになる)からじゃないかと思われる。
3335 if (test_opt(sb, DATA_FLAGS) == EXT4_MOUNT_JOURNAL_DATA) {
3336 printk_once(KERN_WARNING "EXT4-fs: Warning: mounting "
3337 "with data=journal disables delayed "
3338 "allocation and O_DIRECT support!\n");
3339 if (test_opt2(sb, EXPLICIT_DELALLOC)) {
3340 ext4_msg(sb, KERN_ERR, "can't mount with "
3341 "both data=journal and delalloc");
3342 goto failed_mount;
3343 }
3344 if (test_opt(sb, DIOREAD_NOLOCK)) {
3345 ext4_msg(sb, KERN_ERR, "can't mount with "
3346 "both data=journal and dioread_nolock");
3347 goto failed_mount;
3348 }
3349 if (test_opt(sb, DAX)) {
3350 ext4_msg(sb, KERN_ERR, "can't mount with "
3351 "both data=journal and dax");
3352 goto failed_mount;
3353 }
3354 if (test_opt(sb, DELALLOC))
3355 clear_opt(sb, DELALLOC);
3356 } else {
3357 sb->s_iflags |= SB_I_CGROUPWB;
3358 }
kernel/fs/ext4/super.c:parse_options()より、data=orderedだとjournal_async_commitは使えない。d4f761074で入った。
1794 if (test_opt(sb, DATA_FLAGS) == EXT4_MOUNT_ORDERED_DATA &&
1795 test_opt(sb, JOURNAL_ASYNC_COMMIT)) {
1796 ext4_msg(sb, KERN_ERR, "can't mount with journal_async_commit "
1797 "in data=ordered mode");
1798 return 0;
1799 }
kernel/fs/ext4/super.c:ext4_mount_opts[]より、journal_async_commitだとjournal_checksumも立つ。
1420 {Opt_journal_async_commit, (EXT4_MOUNT_JOURNAL_ASYNC_COMMIT |
1421 EXT4_MOUNT_JOURNAL_CHECKSUM),
が、kernel/fs/ext4/super.c:set_journal_csum_feature_set()によると、JOURNAL_ASYNC_COMMITが立っているときはJOURNAL_CHECKSUMは意味をなしていないように見える。他にJOURNAL_CHECKSUMを参照している箇所もなく、jbd2の中でasync_commitが立っているとchecksumも動く、とかいう風にも見えず、何のためなのかわからなかった。
2981 if (test_opt(sb, JOURNAL_ASYNC_COMMIT)) {
2982 ret = jbd2_journal_set_features(sbi->s_journal,
2983 compat, 0,
2984 JBD2_FEATURE_INCOMPAT_ASYNC_COMMIT |
2985 incompat);
2986 } else if (test_opt(sb, JOURNAL_CHECKSUM)) {
2987 ret = jbd2_journal_set_features(sbi->s_journal,
2988 compat, 0,
2989 incompat);
2990 jbd2_journal_clear_features(sbi->s_journal, 0, 0,
2991 JBD2_FEATURE_INCOMPAT_ASYNC_COMMIT);
2992 } else {
2993 jbd2_journal_clear_features(sbi->s_journal, 0, 0,
2994 JBD2_FEATURE_INCOMPAT_ASYNC_COMMIT);
2995 }
kernel/fs/ext4/super.c:ext4_fill_super()より、journalなしだとjournal_async_commitは使えない。・・・はさすがに自明か。
3736 if (test_opt(sb, JOURNAL_ASYNC_COMMIT)) {
3737 ext4_msg(sb, KERN_ERR, "can't mount with "
3738 "journal_async_commit, fs mounted w/o journal");
3739 goto failed_mount_wq;
3740 }
コードを読む
オプションの解釈
kernel/fs/ext4/super.cのtokensとext4_mount_optsより、
1233 {Opt_data_journal, "data=journal"},
1234 {Opt_data_ordered, "data=ordered"},
1235 {Opt_data_writeback, "data=writeback"},
1448 {Opt_data_journal, EXT4_MOUNT_JOURNAL_DATA, MOPT_NO_EXT2 | MOPT_DATAJ},
1449 {Opt_data_ordered, EXT4_MOUNT_ORDERED_DATA, MOPT_NO_EXT2 | MOPT_DATAJ},
1450 {Opt_data_writeback, EXT4_MOUNT_WRITEBACK_DATA,
1451 MOPT_NO_EXT2 | MOPT_DATAJ},
data=writebackの場合はEXT4_MOUNT_WRITEBACK_DATAが、data=orderedの場合はEXT4_MOUNT_ORDERED_DATAが、data=journalの場合はEXT4_MOUNT_JOURNAL_DATAが、それぞれ立つ。
journalの読み込みもまたkernel/fs/ext4/super.c:ext4_fill_super()で行われる。
3717 /*
3718 * The first inode we look at is the journal inode. Don't try
3719 * root first: it may be modified in the journal!
3720 */
3721 if (!test_opt(sb, NOLOAD) && ext4_has_feature_journal(sb)) {
3722 if (ext4_load_journal(sb, es, journal_devnum))
3723 goto failed_mount3a;
3724 } else if (test_opt(sb, NOLOAD) && !(sb->s_flags & MS_RDONLY) &&
3725 ext4_has_feature_journal_needs_recovery(sb)) {
3726 ext4_msg(sb, KERN_ERR, "required journal recovery "
3727 "suppressed and not mounted read-only");
3728 goto failed_mount_wq;
3729 } else {
3730 /* Nojournal mode, all journal mount options are illegal */
3731 if (test_opt2(sb, EXPLICIT_JOURNAL_CHECKSUM)) {
3732 ext4_msg(sb, KERN_ERR, "can't mount with "
3733 "journal_checksum, fs mounted w/o journal");
3734 goto failed_mount_wq;
3735 }
3736 if (test_opt(sb, JOURNAL_ASYNC_COMMIT)) {
3737 ext4_msg(sb, KERN_ERR, "can't mount with "
3738 "journal_async_commit, fs mounted w/o journal");
3739 goto failed_mount_wq;
3740 }
3741 if (sbi->s_commit_interval != JBD2_DEFAULT_MAX_COMMIT_AGE*HZ) {
3742 ext4_msg(sb, KERN_ERR, "can't mount with "
3743 "commit=%lu, fs mounted w/o journal",
3744 sbi->s_commit_interval / HZ);
3745 goto failed_mount_wq;
3746 }
3747 if (EXT4_MOUNT_DATA_FLAGS &
3748 (sbi->s_mount_opt ^ sbi->s_def_mount_opt)) {
3749 ext4_msg(sb, KERN_ERR, "can't mount with "
3750 "data=, fs mounted w/o journal");
3751 goto failed_mount_wq;
3752 }
3753 sbi->s_def_mount_opt &= EXT4_MOUNT_JOURNAL_CHECKSUM;
3754 clear_opt(sb, JOURNAL_CHECKSUM);
3755 clear_opt(sb, DATA_FLAGS);
3756 sbi->s_journal = NULL;
3757 needs_recovery = 0;
3758 goto no_journal;
3759 }
- noloadじゃない、かつ、superブロックにjournalオプションがある場合、journalを読みにいってダメならmountエラー
- noload、かつ、readonlyじゃない、かつ、superブロックにrecoveryオプションがあるなら、mountエラー
- 上記じゃない場合、journalなしモード(sbi->s_journalがNULL)となって各種チェックを行う。
という感じになっている。
EXT4_MOUNT_WRITEBACK_DATA, EXT4_MOUNT_ORDERED_DATA, EXT4_MOUNT_JOURNAL_DATAは、実質的にkernel/fs/ext4/ext4_jbd2.h:ext4_inode_journal_mode()でのみ使われている
392 static inline int ext4_inode_journal_mode(struct inode *inode)
393 {
394 if (EXT4_JOURNAL(inode) == NULL)
395 return EXT4_INODE_WRITEBACK_DATA_MODE; /* writeback */
396 /* We do not support data journalling with delayed allocation */
397 if (!S_ISREG(inode->i_mode) ||
398 test_opt(inode->i_sb, DATA_FLAGS) == EXT4_MOUNT_JOURNAL_DATA)
399 return EXT4_INODE_JOURNAL_DATA_MODE; /* journal data */
400 if (ext4_test_inode_flag(inode, EXT4_INODE_JOURNAL_DATA) &&
401 !test_opt(inode->i_sb, DELALLOC))
402 return EXT4_INODE_JOURNAL_DATA_MODE; /* journal data */
403 if (test_opt(inode->i_sb, DATA_FLAGS) == EXT4_MOUNT_ORDERED_DATA)
404 return EXT4_INODE_ORDERED_DATA_MODE; /* ordered */
405 if (test_opt(inode->i_sb, DATA_FLAGS) == EXT4_MOUNT_WRITEBACK_DATA)
406 return EXT4_INODE_WRITEBACK_DATA_MODE; /* writeback */
407 else
408 BUG();
409 }
410
411 static inline int ext4_should_journal_data(struct inode *inode)
412 {
413 return ext4_inode_journal_mode(inode) & EXT4_INODE_JOURNAL_DATA_MODE;
414 }
415
416 static inline int ext4_should_order_data(struct inode *inode)
417 {
418 return ext4_inode_journal_mode(inode) & EXT4_INODE_ORDERED_DATA_MODE;
419 }
420
421 static inline int ext4_should_writeback_data(struct inode *inode)
422 {
423 return ext4_inode_journal_mode(inode) & EXT4_INODE_WRITEBACK_DATA_MODE;
424 }
inodeごとに判断されるものの、結局、ext4_should_journal_data(), ext4_should_order_data(), **ext4_should_writeback_data()**を確認すればよいとなる。...厳密に言うと、kernel/fs/ext4/inode.c:ext4_set_aops()もあるけど、こっちはどんどん脱線方向になりそうで、今回は省略する...
3533 void ext4_set_aops(struct inode *inode)
3534 {
3535 switch (ext4_inode_journal_mode(inode)) {
3536 case EXT4_INODE_ORDERED_DATA_MODE:
3537 ext4_set_inode_state(inode, EXT4_STATE_ORDERED_MODE);
3538 break;
3539 case EXT4_INODE_WRITEBACK_DATA_MODE:
3540 ext4_clear_inode_state(inode, EXT4_STATE_ORDERED_MODE);
3541 break;
3542 case EXT4_INODE_JOURNAL_DATA_MODE:
3543 inode->i_mapping->a_ops = &ext4_journalled_aops;
3544 return;
3545 default:
3546 BUG();
3547 }
3548 if (test_opt(inode->i_sb, DELALLOC))
3549 inode->i_mapping->a_ops = &ext4_da_aops;
3550 else
3551 inode->i_mapping->a_ops = &ext4_aops;
3552 }
journalモードによる違い
ext4_should_writeback_data()
kernel/fs/ext4/mballoc.c:ext4_free_blocks()で使われている。
4820 /*
4821 * We need to make sure we don't reuse the freed block until after the
4822 * transaction is committed. We make an exception if the inode is to be
4823 * written in writeback mode since writeback mode has weak data
4824 * consistency guarantees.
4825 */
4826 if (ext4_handle_valid(handle) &&
4827 ((flags & EXT4_FREE_BLOCKS_METADATA) ||
4828 !ext4_should_writeback_data(inode))) {
4829 struct ext4_free_data *new_entry;
4830 /*
4831 * We use __GFP_NOFAIL because ext4_free_blocks() is not allowed
4832 * to fail.
4833 */
4834 new_entry = kmem_cache_alloc(ext4_free_data_cachep,
4835 GFP_NOFS|__GFP_NOFAIL);
4836 new_entry->efd_start_cluster = bit;
4837 new_entry->efd_group = block_group;
4838 new_entry->efd_count = count_clusters;
4839 new_entry->efd_tid = handle->h_transaction->t_tid;
4840
4841 ext4_lock_group(sb, block_group);
4842 mb_clear_bits(bitmap_bh->b_data, bit, count_clusters);
4843 ext4_mb_free_metadata(handle, &e4b, new_entry);
4844 } else {
4845 /* need to update group_info->bb_free and bitmap
4846 * with group lock held. generate_buddy look at
4847 * them with group lock_held
4848 */
4849 if (test_opt(sb, DISCARD)) {
4850 err = ext4_issue_discard(sb, block_group, bit, count);
4851 if (err && err != -EOPNOTSUPP)
4852 ext4_msg(sb, KERN_WARNING, "discard request in"
4853 " group:%d block:%d count:%lu failed"
4854 " with %d", block_group, bit, count,
4855 err);
4856 } else
4857 EXT4_MB_GRP_CLEAR_TRIMMED(e4b.bd_info);
4858
4859 ext4_lock_group(sb, block_group);
4860 mb_clear_bits(bitmap_bh->b_data, bit, count_clusters);
4861 mb_free_blocks(inode, &e4b, bit, count_clusters);
4862 }
free(ファイルが消える・小さくなる)時に、metadata(ファイルサイズ)の変更とデータのbitmap変更とのどちらを先にするか、のようだ。
ext4_should_order_data()
2箇所ある。
1つめはkernel/fs/ext4/inode.c:ext4_evict_inode()、ファイルを消すときにファイルサイズを先にゼロにしている。
226 if (ext4_should_order_data(inode))
227 ext4_begin_ordered_truncate(inode, 0);
2つめは、kernel/fs/ext4/inode.c:ext4_setattr()、ファイルサイズを小さくするときに**ext4_begin_ordered_truncate()**を呼ぶ。
5006 if (ext4_should_order_data(inode) &&
5007 (attr->ia_size < inode->i_size)) {
5008 error = ext4_begin_ordered_truncate(inode,
5009 attr->ia_size);
5010 if (error)
5011 goto err_out;
5012 }
ext4_begin_ordered_truncate()は、kernel/fs/jbd2/transaction.c:jbd2_journal_begin_ordered_truncate()を呼ぶ。結局のところ、journalがまだcommitされていなかったらfilemap_fdatawrite_range()を呼ぶ。これはWB_SYNC_ALLで**__filemap_fdatawrite_range()**を呼ぶ、つまり書き出し待ちをする。
2565 if (inode_trans == commit_trans) {
2566 ret = filemap_fdatawrite_range(jinode->i_vfs_inode->i_mapping,
2567 new_size, LLONG_MAX);
2568 if (ret)
2569 jbd2_journal_abort(journal, ret);
2570 }
ext4_should_journal_data()
たくさんあって長々となってしまっているので、気長に読んで...
kernel/fs/ext4/super.c:ext4_quota_on()では、quota処理を開始する前に明示的にflushしている。コメントから察するに、たぶんflushせずにquota処理しようとすると複雑になるんだろう。
5069 /*
5070 * When we journal data on quota file, we have to flush journal to see
5071 * all updates to the file when we bypass pagecache...
5072 */
5073 if (EXT4_SB(sb)->s_journal &&
5074 ext4_should_journal_data(d_inode(path->dentry))) {
5075 /*
5076 * We don't need to lock updates but journal_flush() could
5077 * otherwise be livelocked...
5078 */
5079 jbd2_journal_lock_updates(EXT4_SB(sb)->s_journal);
5080 err = jbd2_journal_flush(EXT4_SB(sb)->s_journal);
5081 jbd2_journal_unlock_updates(EXT4_SB(sb)->s_journal);
5082 if (err)
5083 return err;
5084 }
kernel/fs/ext4/inline.c:ext4_convert_inline_data_to_extent()は、すべてのページごとに**do_journal_get_write_access()関数を呼ぶ。kernel/fs/ext4/inode.c:do_journal_get_write_access()は、いまいち理解しきれないけど、dirtyならext4_handle_dirty_metadata()**を呼ぶことで書き込むタイミングを確定させているっぽい?
590 if (!ret && ext4_should_journal_data(inode)) {
591 ret = ext4_walk_page_buffers(handle, page_buffers(page),
592 from, to, NULL,
593 do_journal_get_write_access);
594 }
1005 /*
1006 * To preserve ordering, it is essential that the hole instantiation and
1007 * the data write be encapsulated in a single transaction. We cannot
1008 * close off a transaction and start a new one between the ext4_get_block()
1009 * and the commit_write(). So doing the jbd2_journal_start at the start of
1010 * prepare_write() is the right place.
1011 *
1012 * Also, this function can nest inside ext4_writepage(). In that case, we
1013 * *know* that ext4_writepage() has generated enough buffer credits to do the
1014 * whole page. So we won't block on the journal in that case, which is good,
1015 * because the caller may be PF_MEMALLOC.
1016 *
1017 * By accident, ext4 can be reentered when a transaction is open via
1018 * quota file writes. If we were to commit the transaction while thus
1019 * reentered, there can be a deadlock - we would be holding a quota
1020 * lock, and the commit would never complete if another thread had a
1021 * transaction open and was blocking on the quota lock - a ranking
1022 * violation.
1023 *
1024 * So what we do is to rely on the fact that jbd2_journal_stop/journal_start
1025 * will _not_ run commit under these circumstances because handle->h_ref
1026 * is elevated. We'll still have enough credits for the tiny quotafile
1027 * write.
1028 */
1029 int do_journal_get_write_access(handle_t *handle,
1030 struct buffer_head *bh)
1031 {
1032 int dirty = buffer_dirty(bh);
1033 int ret;
1034
1035 if (!buffer_mapped(bh) || buffer_freed(bh))
1036 return 0;
1037 /*
1038 * __block_write_begin() could have dirtied some buffers. Clean
1039 * the dirty bit as jbd2_journal_get_write_access() could complain
1040 * otherwise about fs integrity issues. Setting of the dirty bit
1041 * by __block_write_begin() isn't a real problem here as we clear
1042 * the bit before releasing a page lock and thus writeback cannot
1043 * ever write the buffer.
1044 */
1045 if (dirty)
1046 clear_buffer_dirty(bh);
1047 BUFFER_TRACE(bh, "get write access");
1048 ret = ext4_journal_get_write_access(handle, bh);
1049 if (!ret && dirty)
1050 ret = ext4_handle_dirty_metadata(handle, NULL, bh);
1051 return ret;
1052 }
kernel/fs/ext4/indirect.c:ext4_clear_blocks()は、EXT4_FREE_BLOCKS_FORGETを立てることで、**ext4_free_blocks()の中からext4_forget()**を呼ぶようにしている。**ext4_forget()**は、data=journalの場合はjbd2のforgetを呼ぶ、そうでない場合はjbd2のrevokeを呼ぶ。
959 if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))
960 flags |= EXT4_FREE_BLOCKS_FORGET | EXT4_FREE_BLOCKS_METADATA;
961 else if (ext4_should_journal_data(inode))
962 flags |= EXT4_FREE_BLOCKS_FORGET;
kernel/fs/ext4/fsync.c:ext4_sync_file()は、fsync(2)は本来ならデータを書き込むところまで待つべきだが、data=journalモードだとデータはjournalに先に書くので、journalだけ書き出せばよいことになる、からそうしているようだ。
118 /*
119 * data=writeback,ordered:
120 * The caller's filemap_fdatawrite()/wait will sync the data.
121 * Metadata is in the journal, we wait for proper transaction to
122 * commit here.
123 *
124 * data=journal:
125 * filemap_fdatawrite won't do anything (the buffers are clean).
126 * ext4_force_commit will write the file data into the journal and
127 * will wait on that.
128 * filemap_fdatawait() will encounter a ton of newly-dirtied pages
129 * (they were dirtied by commit). But that's OK - the blocks are
130 * safe in-journal, which is all fsync() needs to ensure.
131 */
132 if (ext4_should_journal_data(inode)) {
133 ret = ext4_force_commit(inode->i_sb);
134 goto out;
135 }
kernel/fs/ext4/move_extent.c:ext4_move_extents()は、data=journalだとonline defragができないみたい。ただ現時点(2017/02/08)でもまだTODO:のままだった。ぐぬぬ...ちなみにonline defragていうのはe2fsprogsに含まれるe4defrag(8)というツールのことみたい。ioctlのEXT4_IOC_MOVE_EXTでできるみたい。
592 /* TODO: it's not obvious how to swap blocks for inodes with full
593 journaling enabled */
594 if (ext4_should_journal_data(orig_inode) ||
595 ext4_should_journal_data(donor_inode)) {
596 ext4_msg(orig_inode->i_sb, KERN_ERR,
597 "Online defrag not supported with data journaling");
598 return -EOPNOTSUPP;
599 }
kernel/fs/ext4/inode.c:ext4_evict_inode()は、
消そうとしているファイルはVFS的にcleanと判断されるけどjournal的には残っているので、それをケアしないといけないみたい?
189 if (inode->i_nlink) {
190 /*
191 * When journalling data dirty buffers are tracked only in the
192 * journal. So although mm thinks everything is clean and
193 * ready for reaping the inode might still have some pages to
194 * write in the running transaction or waiting to be
195 * checkpointed. Thus calling jbd2_journal_invalidatepage()
196 * (via truncate_inode_pages()) to discard these buffers can
197 * cause data loss. Also even if we did not discard these
198 * buffers, we would have no way to find them after the inode
199 * is reaped and thus user could see stale data if he tries to
200 * read them before the transaction is checkpointed. So be
201 * careful and force everything to disk here... We use
202 * ei->i_datasync_tid to store the newest transaction
203 * containing inode's data.
204 *
205 * Note that directories do not have this problem because they
206 * don't use page cache.
207 */
208 if (ext4_should_journal_data(inode) &&
209 (S_ISLNK(inode->i_mode) || S_ISREG(inode->i_mode)) &&
210 inode->i_ino != EXT4_JOURNAL_INO) {
211 journal_t *journal = EXT4_SB(inode->i_sb)->s_journal;
212 tid_t commit_tid = EXT4_I(inode)->i_datasync_tid;
213
214 jbd2_complete_transaction(journal, commit_tid);
215 filemap_write_and_wait(&inode->i_data);
216 }
217 truncate_inode_pages_final(&inode->i_data);
218
219 goto no_delete;
220 }
kernel/fs/ext4/inode.c:ext4_write_begin()は、先の**ext4_convert_inline_data_to_extent()**と同じ。
1218 if (!ret && ext4_should_journal_data(inode)) {
1219 ret = ext4_walk_page_buffers(handle, page_buffers(page),
1220 from, to, NULL,
1221 do_journal_get_write_access);
1222 }
kernel/fs/ext4/inode.c:ext4_writepage()は...よくわからない。43ce1d23bあたりで入ったように見えるけど、これ以前の内容も多く、history追跡してもいまいち意図がわからず。mmapされている場合に何かしたいみたい。
2049 if (PageChecked(page) && ext4_should_journal_data(inode))
2050 /*
2051 * It's mmapped pagecache. Add buffers and journal it. There
2052 * doesn't seem much point in redirtying the page here.
2053 */
2054 return __ext4_journalled_writepage(page, len);
kernel/fs/ext4/inode.c:ext4_writepages()は、遅延アロケーションじゃないという決め打ちで簡易コードに分岐させているみたい。
2619 if (ext4_should_journal_data(inode)) {
2620 struct blk_plug plug;
2621
2622 blk_start_plug(&plug);
2623 ret = write_cache_pages(mapping, wbc, __writepage, mapping);
2624 blk_finish_plug(&plug);
2625 goto out_writepages;
2626 }
kernel/fs/ext4/inode.c:ext4_direct_IO()は、data=journalだとO_DIRECTモードは使えない。
3447 /*
3448 * If we are doing data journalling we don't support O_DIRECT
3449 */
3450 if (ext4_should_journal_data(inode))
3451 return 0;
kernel/fs/ext4/inode.c:__ext4_block_zero_page_range()は、先のext4_convert_inline_data_to_extent()と同じ。
3619 if (ext4_should_journal_data(inode)) {
3620 BUFFER_TRACE(bh, "get write access");
3621 err = ext4_journal_get_write_access(handle, bh);
3622 if (err)
3623 goto unlock;
3624 }
3625 zero_user(page, offset, length);
3626 BUFFER_TRACE(bh, "zeroed end of block");
3627
3628 if (ext4_should_journal_data(inode)) {
3629 err = ext4_handle_dirty_metadata(handle, inode, bh);
3630 } else {
3631 err = 0;
3632 mark_buffer_dirty(bh);
3633 if (ext4_test_inode_state(inode, EXT4_STATE_ORDERED_MODE))
3634 err = ext4_jbd2_file_inode(handle, inode);
3635 }
kernel/fs/ext4/inode.c:ext4_setattr()は、inodeが消されかけの時にIO待ちをしている。先にあったのと同じく、data=journalだとデータだけじゃなくjournalも書き出さないといけない。
5054 /*
5055 * Blocks are going to be removed from the inode. Wait
5056 * for dio in flight. Temporarily disable
5057 * dioread_nolock to prevent livelock.
5058 */
5059 if (orphan) {
5060 if (!ext4_should_journal_data(inode)) {
5061 ext4_inode_block_unlocked_dio(inode);
5062 inode_dio_wait(inode);
5063 ext4_inode_resume_unlocked_dio(inode);
5064 } else
5065 ext4_wait_for_tail_page_commit(inode);
5066 }
kernel/fs/ext4/inode.c:ext4_writepage_trans_blocks()は、書き込み予約ページ数を返すために、journalに書かれるデータの分も計上している。
5189 /*
5190 * Calculate the total number of credits to reserve to fit
5191 * the modification of a single pages into a single transaction,
5192 * which may include multiple chunks of block allocations.
5193 *
5194 * This could be called via ext4_write_begin()
5195 *
5196 * We need to consider the worse case, when
5197 * one new block per extent.
5198 */
5199 int ext4_writepage_trans_blocks(struct inode *inode)
5200 {
5201 int bpp = ext4_journal_blocks_per_page(inode);
5202 int ret;
5203
5204 ret = ext4_meta_trans_blocks(inode, bpp, bpp);
5205
5206 /* Account for data blocks for journalled mode */
5207 if (ext4_should_journal_data(inode))
5208 ret += bpp;
5209 return ret;
5210 }
kernel/fs/ext4/inode.c:ext4_page_mkwrite()の先にある方は...よくわからない。**ext4_page_mkwrite()**はDocumentationのLockingによると、read-onlyからwritableに変わるときに呼ばれるエントリ関数らしい...
5526 /* Delalloc case is easy... */
5527 if (test_opt(inode->i_sb, DELALLOC) &&
5528 !ext4_should_journal_data(inode) &&
5529 !ext4_nonda_switch(inode->i_sb)) {
5530 do {
5531 ret = block_page_mkwrite(vma, vmf,
5532 ext4_da_get_block_prep);
5533 } while (ret == -ENOSPC &&
5534 ext4_should_retry_alloc(inode->i_sb, &retries));
5535 goto out_ret;
5536 }
555 ->page_mkwrite() is called when a previously read-only pte is
556 about to become writeable. The filesystem again must ensure that there are
557 no truncate/invalidate races, and then return with the page locked. If
558 the page has been truncated, the filesystem should not look up a new page
559 like the ->fault() handler, but simply return with VM_FAULT_NOPAGE, which
560 will cause the VM to retry the fault.
561
562 ->pfn_mkwrite() is the same as page_mkwrite but when the pte is
563 VM_PFNMAP or VM_MIXEDMAP with a page-less entry. Expected return is
564 VM_FAULT_NOPAGE. Or one of the VM_FAULT_ERROR types. The default behavior
565 after this call is to make the pte read-write, unless pfn_mkwrite returns
566 an error.
kernel/fs/ext4/inode.c:ext4_page_mkwrite()の後にある方は、先の**ext4_convert_inline_data_to_extent()**と同じ。
5579 if (!ret && ext4_should_journal_data(inode)) {
5580 if (ext4_walk_page_buffers(handle, page_buffers(page), 0,
5581 PAGE_SIZE, NULL, do_journal_get_write_access)) {
5582 unlock_page(page);
5583 ret = VM_FAULT_SIGBUS;
5584 ext4_journal_stop(handle);
5585 goto out;
5586 }
5587 ext4_set_inode_state(inode, EXT4_STATE_JDATA);
5588 }
kernel/fs/ext4/ext4_jbd2.c:__ext4_forget()は、data=journalの場合はrevokeではなくforgetを呼ぶようにしている。
206 /* Never use the revoke function if we are doing full data
207 * journaling: there is no need to, and a V1 superblock won't
208 * support it. Otherwise, only skip the revoke on un-journaled
209 * data blocks. */
210
211 if (test_opt(inode->i_sb, DATA_FLAGS) == EXT4_MOUNT_JOURNAL_DATA ||
212 (!is_metadata && !ext4_should_journal_data(inode))) {
213 if (bh) {
214 BUFFER_TRACE(bh, "call jbd2_journal_forget");
215 err = jbd2_journal_forget(handle, bh);
216 if (err)
217 ext4_journal_abort_handle(where, line, __func__,
218 bh, handle, err);
219 return err;
220 }
221 return 0;
222 }
kernel/fs/ext4/extents.c:get_default_free_blocks_flags()は、デフォルトEXT4_FREE_BLOCKS_FORGETにして、先の**ext4_clear_blocks()**の場合へとつながる。
2483 static inline int get_default_free_blocks_flags(struct inode *inode)
2484 {
2485 if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))
2486 return EXT4_FREE_BLOCKS_METADATA | EXT4_FREE_BLOCKS_FORGET;
2487 else if (ext4_should_journal_data(inode))
2488 return EXT4_FREE_BLOCKS_FORGET;
2489 return 0;
2490 }
kernel/fs/ext4/extents.c:ext4_zero_range()の1つめは、**ext4_force_commit()**を呼ぶことで、現在あるトランザクションを強制的にcommitしてそれが終わるまで待つ。
5632 /* Call ext4_force_commit to flush all data in case of data=journal */
5633 if (ext4_should_journal_data(inode)) {
5634 ret = ext4_force_commit(inode->i_sb);
5635 if (ret)
5636 return ret;
5637 }
kernel/fs/ext4/extents.c:ext4_zero_range()の2つめは、journal記録に必要なブロック数の計算。
4866 /*
4867 * In worst case we have to writeout two nonadjacent unwritten
4868 * blocks and update the inode
4869 */
4870 credits = (2 * ext4_ext_index_trans_blocks(inode, 2)) + 1;
4871 if (ext4_should_journal_data(inode))
4872 credits += 2;
4873 handle = ext4_journal_start(inode, EXT4_HT_MISC, credits);
kernel/fs/ext4/extents.c:ext4_collapse_range()は、先と同じく**ext4_force_commit()**を呼んでいる。
5486 /* Call ext4_force_commit to flush all data in case of data=journal. */
5487 if (ext4_should_journal_data(inode)) {
5488 ret = ext4_force_commit(inode->i_sb);
5489 if (ret)
5490 return ret;
5491 }
kernel/fs/ext4/extents.c:ext4_insert_range()は、先と同じく**ext4_force_commit()**を呼んでいる。
5632 /* Call ext4_force_commit to flush all data in case of data=journal */
5633 if (ext4_should_journal_data(inode)) {
5634 ret = ext4_force_commit(inode->i_sb);
5635 if (ret)
5636 return ret;
5637 }
...やっと**ext4_should_journal_data()**の分が終わった...
more study
debugfsのlogdumpが、journalを読む手助けになる。
# debugfs /dev/sa1
debugfs 1.42.13 (17-May-2015)
debugfs: logdump
Journal starts at block 1, transaction 21047
Found expected sequence 21047, type 1 (descriptor block) at block 1
Found expected sequence 21047, type 2 (commit block) at block 18
Found expected sequence 21048, type 1 (descriptor block) at block 19
Found expected sequence 21048, type 2 (commit block) at block 30
Found expected sequence 21049, type 1 (descriptor block) at block 31
(---snip---)
sleuthkitのjlsがあるという情報も見つかったが、ツールがext3以来メンテされていなくてext4に対応してないっぽかった。
あとは、kernel wiki の ext4 Disk LayoutのJBD2関連を参考にしながら・・・というか、jbd2のdescriptorを定義するstructのヘッダとヘキサダンプとを並べてにらめっこするしかない模様。
あとがき
ext4のjournalのモードとそれによる処理の流れが上記のようにちょっとはわかった気になれた。けど、肝心のjournalの中身は全くわからない。結局のところ、実際のjournalというかtransactionというかのあたりは、ブロックというかページというかの単位でjbd2任せにしているので、そっちも読みすすめなければいけない。
またjournalと密接に関連する機能として、metadata_csumやjournal_checksumのチェックサム機能もあり、データ保護の観点ではこれらもきちんと追わないといけない。
私も順を追って理解していくつもりです。REQ_FUAとかREQ_PREFLUSHとかREQ_METAとかREQ_SYNCとか。・・・でもその辺はblock/writeback_cache_control.txtを理解すればいいだけ? ...と思ってたら、a2b809672(v4.10-rc1)からマクロ名というかポリシーが変わっていた。(WRITE_FUA→REQ_FUAとか。) そういうのはソースコード追いづらくなるからヤメテ... 誤解でした、マクロの意味を厳密に再定義しただけで、マクロ名を置き換えたりまではしてませんでした。