NVMe ストレージの揮発性内部キャッシュ

TL;DR
HDD 同様、NVMe ストレージも nobarrier するなら内部キャッシュを無効化したほうがいいケースがあります。

分かる人向けに端的に書くと、NVMe 仕様には volatile write cache の有無と、有効化/無効化切り替え、および有効な場合にデータが消失するケースが明記されています。手持ちの NVMe デバイスの情報を確認して、ファイルシステムに nobarrier つけたいなら、確実に無効化しておきましょう。

以下は詳細。

NVMe というのは PCIe バスに直接つないでデータを保存できるストレージの共通規格で、nvmexpress.org で仕様が公開されています。最新の仕様は rev. 1.3です。

HDD や SSD のようなものではあるんですが、hdparm コマンドで設定することはできず、ベンダが提供するツールか、NVMe 仕様に基づいてコントロールする OSS の github.com/linux-nvme/nvme-cli を使って設定します。この記事では OSS の nvme-cli を使っています。

さて、本題。昨今の Linux のファイルシステムは、write barrier (以下単にバリア)という仕組みが実装され、デフォルトのマウントオプションでは有効になっています。簡単に説明すると、ストレージ内部の揮発性書き込みキャッシュ(volatile write cache)をきちんと書き出して書き込み順序を保証することで、ファイルシステムや fsync(2) を使うアプリケーションのデータ破損を防ぐ仕組みです。詳しくは Red Hat が公開している資料などをご参照ください。

揮発性書き込みキャッシュを書き出してそれを待機するのは、一般に時間がかかります。そのため fsync(2) を多用するデータベースのようなアプリケーションでは、バリアが有効である場合性能が悪化する場合があります。ファイルシステムによっては、この性能劣化を抑えるために nobarrier というマウントオプションを提供していることがあります。*1

バリアはデータを安全に書き出すための仕組みですので、無効化しても安全に使うためにはストレージデバイスが揮発性書き込みキャッシュを持っていないか、揮発性書き込みキャッシュを無効化していることが条件です。例えば RAID コントローラーでは、キャッシュの内容が電源断でも揮発しないようにバッテリを持っていて、不揮発性キャッシュ(non-volatile write cache)としている場合が多くあります。

バリアを無効化するため、HDD や SSD が持つ揮発性書き込みキャッシュを無効化する場合、一般に hdparm コマンドが使われます。

$ sudo hdparm -W0 /dev/sda

/dev/sda:
 setting drive write-caching to 0 (off)
 write-caching =  0 (off)

hdparm は NVMe ストレージには対応していません。NVMe ならもしかして仕様上、揮発性書き込みキャッシュは存在しないことになっていて、もしかすると無効化操作は必要ないのかもしれません。と思って仕様書を調べたところ、rev. 1.3 の Figure 109 (Identity Controller Data Structure) 525 byte 目に、volatile write cache の有無のフラグがありました。

Volatile Write Cache (VWC):

This field indicates attributes related to the presence of a volatile write cache in the implementation.

Bits 7:1 are reserved. Bit 0 if set to ‘1’ indicates that a volatile write cache is present. If cleared to ‘0’, a volatile write cache is not present.

If a volatile write cache is present, then the host may issue Flush commands and control whether the volatile write cache is enabled with Set Features specifying the Volatile Write Cache feature identifier.

If a volatile write cache is not present, Flush commands complete successfully and have no effect, Set Features with the Volatile Write Cache identifier field set shall fail with Invalid Field status, and Get Features with the Volatile Write Cache identifier field set should fail with Invalid Field status.

コマンドで確認してみると、以下のようになりました。

$ sudo nvme id-ctrl -H /dev/nvme0 | grep 'vwc\|Volatile'
vwc     : 0
  [0:0] : 0     Volatile Write Cache Not Present

この NVMe デバイスには、揮発性書き込みキャッシュはないようですね。もしこの値が 1 の場合は、仕様書 5.21.1.6 にある通り Feature ID 06h で状態を確認したり、無効化したりできます。

5.21.1.6 Volatile Write Cache (Feature Identifier 06h), (Optional)

This Feature controls the volatile write cache, if present, on the controller. If a volatile write cache is supported, then this feature shall be supported. The attributes are indicated in Command Dword 11.

Note: If the controller is able to guarantee that data present in a write cache is written to non-volatile media on loss of power, then that write cache is considered non-volatile and this setting does not apply to that write cache. In that case, this setting has no effect.

If a Get Features command is submitted for this Feature, the attributes specified in Figure 142 are returned in Dword 0 of the completion queue entry for that command.

手持ちの NVMe デバイスは揮発性書き込みキャッシュがないので確認だけですが、以下のようになりました。

$ sudo nvme get-feature /dev/nvme0 --feature-id=6
get-feature: 0x06 (Volatile Write Cache), Current value: 00000000

というわけで、nobarrier マウントオプションを付けて NVMe ストレージを使っている場合、揮発性書き込みキャッシュの有無を確認して、もしあるなら無効化しておきましょう。

*1:ext4 や btrfs は nobarrier あります。XFS は 4.10 から nobarrier 無視するようになりました