btrfs update 2019

# はじめに
ここ数年のbtrfsの開発はバグフィックスや性能向上、コードのクリーンアップに重点が置かれており、あまり積極的に新規機能は追加されない傾向があります。しかしながらこの１年は比較的多くの機能追加が見られました。ここではこの1年(kernel v5.0 ~ v5.5)にbtrfsへ追加された機能や現在メーリングリストに投稿されているパッチの内、特に一般ユーザーの目に付きやすいものをまとめます。

# v5.0 ~ v5.5の間にマージされた機能(抜粋)
### swapfile (v5.0, [patch](https://lore.kernel.org/linux-btrfs/cover.1538072009.git.osandov@fb.com/))
一部制限(シングルデバイスでなければならない等)はあるものの、btrfs上でswapfileの利用ができるようになりました。swapfileを用いたhibernationも可能です。実際に使いたい人は[別の記事](https://qiita.com/tmsn/items/41bf294728a4d2c0d4b8)を参考にしてください。
 
### zstd圧縮レベル (v5.1, [patch](https://lore.kernel.org/linux-btrfs/20190204202008.51652-1-dennis@kernel.org/))
透過圧縮でzstdを利用する場合、その圧縮レベルをマウントオプションにより指定することが可能になりました (例: ```mount <dev> -o compress=zstd:7 /mnt```)。
レベルは1~15まで選べ、数字が大きいほど圧縮率が高くなりますが、その代わり圧縮時間とメモリ使用量が増大します。0 or 何も指定しない場合はデフォルトの圧縮レベル(現在は3)が使用されます。レベルによる圧縮率の差は上のパッチのリンクにあるコミットログを参考にしてください。

### diskへのwrite直前の一貫性チェックの追加 (v5.2, [patch](https://lore.kernel.org/linux-btrfs/20190320062749.30953-1-wqu@suse.com/))
ディスクへwriteをする直前にもデータの一貫性に問題が無いかをチェックするコードが追加されました。これによりディスクへ書き込む前にビットフリップ等の検出を行うことができます。

### scrubの予想終了時間の表示 (v5.2, [patch](https://github.com/kdave/btrfs-progs/commit/832da8ca1de57bc0d852e51e8d7ea7e557052af2))
これはユーザーツール(btrfs-progs)のアップデートですが、```btrfs scrub status```の出力が改訂され、予想終了時間が表示されるようになりました。

### grubによるbtrfs raid5/6およびzstd圧縮のサポート (grub 2.04 [patch](https://lore.kernel.org/linux-btrfs/20181018175541.9532-1-kreijack@libero.it/), [patch](https://lore.kernel.org/linux-btrfs/20181119192008.3496427-1-terrelln@fb.com/))
これはgrubのアップデートですが、raid5/6およびzstd透過圧縮を利用したbtrfsからブートできるようになりました(逆に言うと、grubが古い場合にブートファイルにzstd圧縮を利用するとブートできなくなるので注意が必要です)。

### 3台/4台構成のRAID１ (v5.5, [patch](https://lore.kernel.org/linux-btrfs/cover.1572534591.git.dsterba@suse.com/))
btrfsでraid1プロファイルを利用する場合、ファイルシステムを構成するディスクの数に関係なくその中のいずれか2台に同一のデータの書き込みが行われます。今回新たに追加されたプロファイルであるraid1c3/raid1c4を使用すると、同一のデータが3台または4台のデバイスへ書き込まれるようになります。
　
mkfs時に新規プロファイルを選択できるようになったほか(例: ```mkfs.btrfs -m raid1c3 -d raid6 <dev>```)、balanceにより現在利用中のファイルシステムの構成を変更することも可能です (例: ```btrfs balance start -dconvert=raid1c3,profiles=raid1```)。なお新しいプロファイルを利用する場合は古いkernelでマウントができなくなります。
　
これは単に冗長性を増やしたい場合に利用することもできますが、想定する利用の一つとしてはデータraid6, メタデータraid1c3の組み合わせで利用するというものです。btrfsのraid5/6には大きな未解決の問題としてwrite holeが存在する(大雑把に言うと停電とディスク故障が重なるとファイルシステムが壊れる可能性がある[^1][^2])というものがあります。これを完全解決するためにはwrite ahead logを実装するなどの必要があるのですが、当面の対策としてメタデータをraid1(データがraid5の場合)あるいはraid1c3(データがraid6の場合)とすることで、メタデータに関してはwrite holeの問題をなくし、ファイルシステムが壊れることを防ぐことができます (当然ながらraid1c3の方がraid6に比べて容量効率は低下しますが、通常メタデータの量はデータの量に比べてずっと小さいため影響は少ないはずです。またファイルはRAID構成に関係なくバックアップしましょう)。

[^1]: raid5/6ではデータおよびパリティを複数台のディスクに分散して書き込みます。write中に停電やシステムクラッシュが発生すると、書き込みが完了していないディスクがある場合はデータとパリティが一致しなくなります。ディスクがすべて揃っている場合は再起動直後にscrubを行うことで一貫性のチェックが可能です。しかしここでディスク障害が重なるとパリティが一致しているかどうかを検証することができず、更にそのままデータの再構成を行うと誤ったデータの復元(= データ破損)となる恐れがあります。仮にファイルシステムのメタデータに対してこの状況が発生するとファイルシステムが壊れ、マウントができなくなります。この問題の解決策の１つはwite ahead logを用意する(ログ領域に書き込みを行った後に実際のデータの書き込みを行い、クラッシュ等が発生した場合は再起動時にログをリプライする)ことですが、データを2重に書き込むことになるため処理速度が大きく犠牲になります。
[^2]: (更に補足) btrfsはCopy on Writeをしている = データを直接上書きしないため、どの時点までのデータが書かれたかが分かるのでwrite holeはないのではと思う人がいるかもしれません。実はraid5/6を使用した場合はbtrfsでもread-modify-writeによる上書きが起こります。これはbtrfsがCoWをするサイズが必ずしもraidを構成するストライプのサイズと一致しないためです。なお同じくCoWを行うZFSのRAID-Zではwriteサイズに応じてストライプ長を変化させる = 書き込むディスク数を変化させることでread-modify-writeをなくし、常に新規領域にデータを書き込むことでwrite holeの問題を回避しています。

### 新規チェックサムアルゴリズム (v5.5 [patch](https://lore.kernel.org/linux-btrfs/20191007091104.18095-1-jthumshirn@suse.de/), [patch](https://lore.kernel.org/linux-btrfs/cover.1571674940.git.dsterba@suse.com/))
btrfsでは基本的にすべてのメタデータおよびデータにチェックサムがついています。これまでサポートされているチェックサムはcrc32cのみでしたが、新たに3つのアルゴリズムが追加されました:

 - xxhash (64bit): crc32cよりも高速
 - sha256 (256bit): 暗号学的ハッシュ関数, FIPS準拠だが最も低速
 - blake2b-256 (256bit): 暗号学的ハッシュ関数, sha256よりも高速

使用するチェックサムはmkfs時に選択が可能です (例: ```mkfs.btrfs --csum blake2 <dev>```)。なお追加されたチェックサムアルゴリズムを使用する場合は古いkernelでマウントができなくなります。簡単な各チェックサムアルゴリズムの比較は[こちら](https://lore.kernel.org/linux-btrfs/20191021164549.15404-1-dsterba@suse.com/)を参考にしてください。


# 開発中のもの
現在メーリングリストに投稿されている、特に大きな機能追加のパッチとしては以下のようなものがあります(いつ頃マージされるかは不明です)。

### 非同期discard ([patch](https://lore.kernel.org/linux-btrfs/cover.1574709825.git.dennis@kernel.org/))
現在同期的に行われているdiscard(trim)が非同期で行われるようになり、性能が向上します。

### 透過圧縮をバイパスするread/write ([patch](https://lore.kernel.org/linux-btrfs/cover.1574273658.git.osandov@fb.com/))
透過圧縮を利用している場合に、(kernelでの圧縮処理をバイパスして)圧縮したデータを直接読み書きすることが可能になります。現状では透過圧縮されたデータをsend/receiveするときに通常のread/write処理を行うため、一度データの展開/圧縮処理を挟む必要があります。この機能を利用することで圧縮されたデータを直接send/receiveできるようになり、処理効率が上がります。

### zoned device ([patch](https://lore.kernel.org/linux-btrfs/20190823101036.796932-1-naohiro.aota@wdc.com/))
zoned deviceで直接btrfsファイルシステムを利用することができるようになります。zoned deviceには必ずシーケンシャルwriteを行わなければならないという制約があるため、データの上書きをせずにCoWを行うbtrfsとは相性が良いと言えます(zoned deviceについてはWDの[ページを参照](https://zonedstorage.io/linux/fs/)してください)。

### fsdax ([archive](https://lore.kernel.org/linux-btrfs/?q=dax))
fsdaxのサポートを目指すパッチもいくつか投稿されています。しかしながら解決が必要な問題も数多くあり、完全な実現へはまだ時間がかかりそうです。
(そもそもなぜbtrfs(基本CoWする)でfsdax(ページキャッシュを利用せずにデバイス上のデータを直接読み書きする)を利用する必要があるのかという話はありますが、subvolumeやreflink, send/receiveといったbtrfsの機能が使えるようになれば他のFSにない利点が出てくると思います。)


# おわりに
ここに挙げたのは一般ユーザーが興味を惹きやすい機能のみです。実際にはこれらよりも遥かに多くのバグフィックス・性能改善・btrfs checkの機能向上・小さな機能追加・メッセージ修正・クリーンアップ等が行われています。気になる方はwikiの[changelog](https://btrfs.wiki.kernel.org/index.php/Changelog)または[メーリングリスト](https://lore.kernel.org/linux-btrfs/)を参照してください。またbtrfsを利用する場合はなるべく最新のkernel, btrfs-progsを利用するほうが良いでしょう。