LoginSignup
6
5

More than 5 years have passed since last update.

ext4のnoauto_da_allocオプションの詳細

Last updated at Posted at 2017-01-13

まずは結論

Linux-3.18くらいを見て書いてます。

よくわからない場合

オプションは何も足すな。何も削るな。

わかった上でパフォーマンス上げたい場合

noauto_da_alloc を足す。

アプリの実装ミスであろうがなかろうが、電源断でファイルが消えるのが怖くて夜も寝られない場合

journal_checksum data=journal くらい?
(data=journalにすると自動でnodelallocも有効になる)
(nodelallocだとnoauto_da_allocは意味をなさない)

電源断?そんなのいいから最速を目指すぜ

noauto_da_alloc nobarrier journal_async_commit noatime data=writeback くらい?
・・・tune2fs(8)使ってjournalなしにする?むしろext4なんてやめた方が・・・

はじめに

2009年-2010年頃にいわゆる「Zero Length File Problem」で世間が賑やかになった結果、noauto_da_allocnodelalloc オプションが注目を集めた。ググれば英語の情報はそれなりにあるものの、日本語ではほとんどなかったので、ここに一度まとめておくこととした。

誤解してほしくないのは、下記はすべて電源断(や突然のpanicやリセットやディスク/ケーブル引っこ抜き)の時を想定した話。不正規のshutdownがないとした場合、どんなmountオプションであったとしても「Zero Length File Problem」のようなことは起こらない。

また、ディスク上のデータ化け、CPUの暴走、RAM化けはそもそも考慮外。これらが起こると、Filesystemがどうこうじゃないレベルでデータが壊れる。

このページを信じてはいけない

ググると日本語では上記ページが上位にヒットする。通りすがりの方々がコメントで指摘してる通りで、デフォルトはauto_da_alloc(==「壊れたアプリ」の面倒を見るためにわざわざKernelで特殊対応)の動作をする。このわざわざ行っている特殊対応を行わないようにするのがnoauto_da_allocオプションとなる。

noauto_da_alloc を足すと何が起こるの?

Documentationを見る

Documentation/filesystems/ext4.txtにはものすごく具体的なことが書いてある。おそらく開発者自身がいろいろ言い訳したかったんだろう。

Many broken applications don't use fsync() when
replacing existing files via patterns such as
fd = open("foo.new")/write(fd,..)/close(fd)/
rename("foo.new", "foo"), or worse yet,
fd = open("foo", O_TRUNC)/write(fd,..)/close(fd).
If auto_da_alloc is enabled, ext4 will detect
the replace-via-rename and replace-via-truncate
patterns and force that any delayed allocation
blocks are allocated such that at the next
journal commit, in the default data=ordered
mode, the data blocks of the new file are forced
to disk before the rename() operation is
committed. This provides roughly the same level
of guarantees as ext3, and avoids the
"zero-length" problem that can happen when a
system crashes before the delayed allocation
blocks are forced to disk.

更新されないままのドキュメントが多いから本当はリンクしたくないんだけど、一応日本語の方も。
https://linuxjf.osdn.jp/JFdocs/kernel-docs-2.6/filesystems/ext4.txt.html

多くの壊れたアプリケーションは、fd = open("foo.new")/
write(fd,..)/close(fd)/rename("foo.new", "foo") な
どのパターン、あるいはそれより邪悪な
fd = open("foo", O_TRUNC)/write(fd,..)/close(fd)
パターンで、fsync() を使わないで既存のファイルを入れ
替えます。auto_da_alloc が有効な場合、ext4 はこのリ
ネームによるファイルの入れ替えと truncate によるファ
イルの入れ替えのパターンを検知し、標準のデータオーダー
ドモードの場合に、リネーム処理がコミットされる前に新規
ファイルのデータブロックがディスクに書き込まれるよう、
遅延割り当てされたブロックを次のジャーナルコミット時に
強制的に割り当てます。この処理でほぼ ext3 と同程度の保
証ができ、遅延割り当てされたブロックがディスクに書かれ
る前にシステムがクラッシュした場合の「長さ 0 になる」
問題を (同程度に) 回避できます。

コードを見る

super.cのext4_mount_optsより、

super.c
1379         {Opt_noauto_da_alloc, EXT4_MOUNT_NO_AUTO_DA_ALLOC, MOPT_SET},
1380         {Opt_auto_da_alloc, EXT4_MOUNT_NO_AUTO_DA_ALLOC, MOPT_CLEAR},

なので、noauto_da_allocオプションを入れるとEXT4_MOUNT_NO_AUTO_DA_ALLOCが立つことがわかる。
マクロ展開されるため、EXT4_MOUNT_NO_AUTO_DA_ALLOC ではなく NO_AUTO_DA_ALLOC でgrepすると、2箇所ヒットする

1箇所目がnamei.cのext4_rename()

namei.c
3576         if (new.inode && !test_opt(new.dir->i_sb, NO_AUTO_DA_ALLOC))
3577                 ext4_alloc_da_blocks(old.inode);

renameで上書きが発生する場合にold側(==名前が変わる方のinode)を対象にext4_alloc_da_blocks()が呼ばれる。

2箇所目がinode.cのext4_truncate()

inode.c
3584         if (inode->i_size == 0 && !test_opt(inode->i_sb, NO_AUTO_DA_ALLOC))
3585                 ext4_set_inode_state(inode, EXT4_STATE_DA_ALLOC_CLOSE);

EXT4_STATE_DA_ALLOC_CLOSEはfile.cのext4_release_file()

file.c
 38         if (EXT4_I(inode)->i_state & EXT4_STATE_DA_ALLOC_CLOSE) {
 39                 ext4_alloc_da_blocks(inode);
 40                 EXT4_I(inode)->i_state &= ~EXT4_STATE_DA_ALLOC_CLOSE;
 41         }

つまり、truncateでファイルサイズを0にした後closeしたタイミングで、ここでもext4_alloc_da_blocks()が呼ばれる。

inode.cのext4_alloc_da_blocks()はコメントが長いけどそのまま引用して、

inode.c
2703 int ext4_alloc_da_blocks(struct inode *inode)
2704 {
2705         trace_ext4_alloc_da_blocks(inode);
2706 
2707         if (!EXT4_I(inode)->i_reserved_data_blocks)
2708                 return 0;
2709 
2710         /*
2711          * We do something simple for now.  The filemap_flush() will
2712          * also start triggering a write of the data blocks, which is
2713          * not strictly speaking necessary (and for users of
2714          * laptop_mode, not even desirable).  However, to do otherwise
2715          * would require replicating code paths in:
2716          *
2717          * ext4_writepages() ->
2718          *    write_cache_pages() ---> (via passed in callback function)
2719          *        __mpage_da_writepage() -->
2720          *           mpage_add_bh_to_extent()
2721          *           mpage_da_map_blocks()
2722          *
2723          * The problem is that write_cache_pages(), located in
2724          * mm/page-writeback.c, marks pages clean in preparation for
2725          * doing I/O, which is not desirable if we're not planning on
2726          * doing I/O at all.
2727          *
2728          * We could call write_cache_pages(), and then redirty all of
2729          * the pages by calling redirty_page_for_writepage() but that
2730          * would be ugly in the extreme.  So instead we would need to
2731          * replicate parts of the code in the above functions,
2732          * simplifying them because we wouldn't actually intend to
2733          * write out the pages, but rather only collect contiguous
2734          * logical block extents, call the multi-block allocator, and
2735          * then update the buffer heads with the block allocations.
2736          *
2737          * For now, though, we'll cheat by calling filemap_flush(),
2738          * which will map the blocks, and start the I/O, but not
2739          * actually wait for the I/O to complete.
2740          */
2741         return filemap_flush(inode->i_mapping);
2742 }

i_reserved_data_blocksは遅延アロケーションしているブロックの数。これがなければ何もしない。これがあればfilemap_flush()を呼ぶ。つまり、IOをスタートさせる(けどIO完了までは待たない)。fsyncを呼ぶのに近いことをしている。

(「時々目的のpageのIOが依存の影響で発生しないこともある」とかコメントに書いてあったけど今は気にしないことにする...)

まとめると

ext4はデフォルトでは、
- renameで上書きが発生する場合
- truncateでサイズ0にした場合のcloseのタイミング

- fsyncに近いこと
をする。ただし書き込み待ちはしない。こういう動きをすることで「Zero Length File Problem」を起こしにくくしている。noauto_da_allocオプションを足すと、この特別対応を行わなくする。

auto_da_allocがあるから「Zero Length File Problem」はもう起こらないんだよね?

答えはNO、EXT4 VS. EXT3 AND WHY DELAYED ALLOCATION IS BADに詳しい。

これはKernel(Filesystem)のバグではなくてユーザプログラムのバグなのである。「Many broken applications」は「ext3の時の挙動こそ正しい」と信じて「ext4の動きがおかしい」と言ってるだけ。auto_da_allocで上記2パターンに救済を入れたものの、これだけではext4がext3と近い挙動にはならない。少なくとも遅延アロケーション機能を無効にしないと近い挙動にはならない。

というわけで、「Many broken applications」をそのまま使い続けたい場合nodelallocが推奨されることになる。ただしパフォーマンスが1割前後落ちる。

世の中ext3/ext4だけではないので、結局POSIXレベルで議論する限り、複数のinodeをまたいでデータの更新の順序を保障したい場合、fsync(2)fsyncdata(2)をアプリが適切に呼ぶことでしか保障できない。

あとがき

電源断を含めたデータの保護はものすごく難しい。ストレージのハードウェア(やファームウェア)がやるべきこと、ストレージドライバがやるべきこと、Filesystemがやるべきこと、アプリケーションがやるべきこと、いろいろある。ACIDだトランザクションだ言われる。だからRDBMSがもてはやされるんだと思う。

今後もext4シリーズの記事を書く方向へ続くかもしれない?

6
5
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
5