Linuxの不揮発メモリ(NVDIMM)対応動向について(2018年版)

#(2019/6/12追記)
2019年1月までの3年分の概説を、以下のスライドにまとめてあります。こちらをご参照ください。
https://www.slideshare.net/ygotokernel/nvdimmlinux-137104084

#はじめに

この記事は[Fujitsu Advent Calendar 2018](https://qiita.com/advent-calendar/2018/fujitsu)の24日目の記事です。
昨年までに引き続き、今年も不揮発メモリ(NVDIMM)について取り上げ、2018年の状況を概観したいと思います。

### お断り（お約束）

本記事の内容は私個人の見解であり、所属する組織として意見を代表したり保証したりするものではありません。
内容については誤りを含む可能性もありますのでご注意ください。(誤りについてご指摘していただけると助かります。)

#IntelのNVDIMMがお披露目
IntelのNVDIMMの正式名称が[Optane DC Persistent Memory] (https://www.intel.co.jp/content/www/jp/ja/products/docs/memory-storage/optane-persistent-memory/what-is-intel-optane-dc-persistent-memory-video.html)として発表され、ようやく正式出荷にむけて本格的に動き出しました。「ほんとに出るの？」と疑問の声のあった昨年までと比べると大きく前進したように思います。

#NVDIMMのDBへの適用

NVDIMMはDRAMと比べるとやや速度が遅いなど、独特の癖があるので使いどころが難しいという評価を受けがちです。しかし、今年はDBへの適用が進んできたように思います。なんといってもインメモリDBであるSAP-HANAが、すでに製品として正式対応を始めています。
[SAP HANAのAdministration Guide](https://help.sap.com/doc/eb75509ab0fd1014a2c6ba9b6d252832/2.0.03/en-US/SAP_HANA_Administration_Guide_en.pdf)を参照すると、6.7.4.3 Persistent Memory "At the level of the Linux operating system, SAP HANA only supports DAX enabled (Direct Access) file systems."としっかり記述されています。
NVDIMM界隈（？）にとっては大きなニュースと言えるでしょう。

SAP HANAがNVDIMMをどのように使っているかは、以下のpublickeyの解説がわかりやすいでしょう。
[インメモリDBの「SAP HANA」、インテルの不揮発性メモリに正式対応。サーバダウン時もデータを失わず高速起動](https://www.publickey1.jp/blog/18/dbsap_hana.html)



また、先日公開されたSLES12 SP4のRelease Noteでも["Support for SAP HANA Workloads on Intel Optane DC Memory"](https://www.suse.com/releasenotes/x86_64/SUSE-SLES/12-SP4/#fate-325647)と明記されました。NVDIMMの時代が着実に見えてきたといえるでしょう。

研究レベルでもDBへのNVDIMMの適用研究も近年は盛んにおこなわれています。専門外なので解説は他に譲りますが、解説・論文・発表資料など、いくつか知っているものをポイントします。後ろの２つは今年の発表になります。

- [トランザクションの設計と進化(kumagiさん)](https://www.slideshare.net/kumagi/ss-64459138)
- [FOEDUS: OLTP Engine for a Thousand Cores and NVRAM (SIGMOD2015) (HPE)](http://www.hpl.hp.com/techreports/2015/HPL-2015-37.pdf)
- [Introducing PMDK into PostgreSQL (PGcon 2018) (NTT)](https://www.pgcon.org/2018/schedule/events/1154.en.html)
- [Managing Non-Volatile Memory in Database Systems (SIGMOD18) (Fujitsu Lab. & Munich Univ.)](https://db.in.tum.de/~leis/papers/nvm.pdf)


#NVDIMM用ライブラリPMDK

今年になって、PMDKをようやく少しさわることができました。代表的なところでは、PMDKは以下のようなライブラリを持っています。

- libpmem: 低レイヤライブラリで、DAX上のファイルへのmmap()やCPU Cacheのflushなどを行うためのものです。特に、IntelはCPU cacheのflushのための命令を新たに２つ(clflushopt, clwb)追加[^1]していますが、それらを環境に合わせて利用可能な命令を適切に呼び出してくれるようになります。
- libpmemobj: Intelイチオシの高レイヤライブラリです。NVDIMM上の各データの更新の際に、逐一[トランザクション](http://pmem.io/2015/06/15/transactions.html)処理を行えるようになるため、突然の電源断などへの対応がアプリ側でできるようになります。NVDIMM上にtreeなどのデータ構造を配置する場合、そのポインタなどの更新の際に必要になってくるでしょう。ただ、[チュートリアル](http://pmem.io/pmdk/libpmemobj/)を概観してもこのライブラリのインタフェースを使いこなすのはかなり難しい印象です。
- libpmemcto: アプリの終了時にデータの永続性を保証し、再起動後に格納したデータを読み出すだけのインタフェースですが、libpmemobjと比べるとシンプルでわかりやすいインタフェースです。
- libvmem: NVDIMMは不揮発性だけでなく、従来のSDRAMと比べて容量が大きいという側面もあるため、NVDIMMを揮発性の領域として使いたいという需要もあります。そのためにDAXの領域を揮発メモリ的に使うことができるようになるライブラリです。
- libvmemmalloc: 環境変数LD_PRELOADを指定することで、malloc()をフックしてRAMの代わりにNVDIMMのDAXの領域を揮発的メモリのように使うことができます。
- librpmem: NVDIMM上のデータを他のシステムにRDMAで転送するためのライブラリで、利用にはPMDKのほかにlibfablicのインストールが必要です。今のところ本ライブラリはexperimentalとなっています。（これは、後述のkernel側の事情によるものと考えられます。）

ただし、libpmemctoは先日品質が悪いということで、Intelはいきなり[廃止して](http://pmem.io/2018/10/22/release-1-5.html)しまいました。また、揮発メモリ的に使うライブラリlibvmemも、[memkind](https://github.com/memkind/memkind)を使うように推奨されるようになってしまいました。こういった点についてはまだ注意が必要です。

#NVDIMMを交換する難しさ

実は、NVDIMMというのはHDD/SSDと比べて__交換が難しい__という問題があることが分かっています。（っていうかLinuxのNVDIMMのメンテナーであるDan Williamsに質問・指摘したのはほかならぬ私ですが…[^2]）。これは以下のような問題点があるからです。

- SSD/HDDのように筐体の外から挿抜することができません。DIMMと同じように、交換するためには筐体の蓋を開けて、マザーボード上に刺さっているNVDIMMを抜く必要があります。つまり、事実上hotplugができません。
- サーバー向けの筐体では、交換するHDD/SSDの物理的な位置を特定するため、IDランプを光らせることができます。では、NVDIMMではどうなるでしょうか？ 筐体を空けた時に/dev/pmemXのデータがどのDIMM slotのNVDIMMに刺さっていたのかがわかる必要がありますが、通常は筐体の蓋を開ける前に電源を落とさざるを得ないので、マザーボード上に/dev/pmemXに対応するIDランプを光らせるというのも難しそうです。
- NVDIMMのRegionとNamespaceについては[昨年述べました](https://qiita.com/YasunoriGoto1/items/367e1d3214fe951d0493)が、これがさらにこの問題を深刻にします。DIMMの領域がRegion/Namespaceで区分けされているうえ、Interleaveもされてしまうので、どのnamespace(すなわち/dev/pmemX)がどのDIMMに対応するのかが非常に分かりにくいのです。
- mirroringもNVDIMMではあまり有効とは言えません。HDDと違い書き込み回数によって劣化していくデバイスでは、mirrorによってそれぞれのデバイスに同じようにデータが書き込まれ同じように劣化していきますから、冗長性の担保があまりできません。また、mirrorするNVDIMMも同じ筐体内に存在するわけですから、結局交換する際にはシステムの電源を停止させて筐体を開けるしかありません。

また、NVDIMM内部には故障ブロックに対するスペアがあることがACPIのNVDIMM deviceに対する[_DSMメソッド](http://pmem.io/documents/NVDIMM_DSM_Interface-V1.8.pdf)のGetting SMART Health Infoの出力からわかりますが、この値を監視してスペアが無くなる前にNVDIMMを交換できたほうが良いでしょう。

これらの問題に対して、我々は以下の開発を行い、ndctlコマンドに機能を追加しました。

- ndctl listコマンドに対して、NVDIMMのハード情報と壊れた領域の関連付けができるように情報を追加しました。
- ndctl monitorコマンドを追加してNVDIMMを監視するデーモンを起動できるようにしました。NVDIMMのステータスを監視し、スペアの残りが閾値を超えた時など、状態変化時にログを出力するデーモンを起動できるようにしました。

これついては、中国での発表資料も参考にしてください。
[The ideal and Reality of NVDIMM RAS](https://www.slideshare.net/ygotokernel/the-ideal-and-reality-of-nvdimm-ras-newer-version)

#Filesystem DAXの課題

さて、昨年の記事でFilesystem DAXがExperimentalであることを述べましたが、残念ながら2018年はまだそのステータスのままになってしまいました。（一時はもう少しで外れるかな？というところまで話が進んでいたはずなのですが…。）
コミュニティでの議論を追うと、細かいコーナーケースのところで問題があるようです。
（私の理解が間違っていなければ）その中で特に次の２つが比較的大きい問題のようです。

### 1.XFSのReflink機能とFilesystem DAXの相性
XFSのreflink機能、つまりファイルをコピーするときにデータをCopy On Writeでコピーする[^3]機能がFilesystem DAXとは相性が悪いことが分かっています。理由の一つとして、Reflinkの機能はDelayed allocation のしくみを使って実現しているようですが、その一方、DAXは即座にFilesystemのblockマッピングを定める必要が機能ですので、そもそもやりたいことが相反しているためのようです。

またさらに、reflink機能は、あるファイルのoffset 100にあるデータを他のファイルのoffset 0にコピーするといった器用なことができるそうです。今まではfilesystemでこれを管理していたのですが、DAXではメモリ管理にも手を入れる必要があります。具体的にいうと、物理メモリを管理するpage structのindexに、マップしているファイルのoffset情報が入っていたのですが、reflink + Filesystem DAXでは、異なるファイルのoffsetが１つの物理メモリを共有してしまうことになるため、page structのindexだけではそれが記録できないのです。

今のところ、reflink機能とFilesystem DAXは排他の関係になっていて、reflink機能を有効にしてmkfsするとFilesystem DAX機能をonにしてmountできないようになっています。このため一般のユーザがこの問題でトラブルが起きるということは無いのですが、Filesystem DAXの機能的にはreflinkとの両立が不可欠と考えられているようです。

前者の問題についてはMLでの議論を見る限り、Dave ChinnerはCoWの時には直接NVDIMM上にマップせず、一度RAM上にデータを配置することを考えているようです。ただ、DAXの特徴である”アプリからcpu cacheのflushをするとデータの永続性が保証される”という機能をRAMでもNVDIMMと同じレベルで実現しなければなりません。実現に至るまではちょっと大変そうです。

また、後者については今のところ解決方法が見つかっていません。

### 2.(R)DMA転送するデバイスとの相性が悪い

デバイスがDMAあるいはRMDAでデータ転送中のNVDIMM上の領域を、ユーザやアプリがtruncate()で削除してしまったらどうしましょうか？Filesystem DAXでは長らくこの問題が付きまとっていました。2018年現在では一般のユーザには問題が発生しないようにするため、Dan Williamsによって以下のような対策が取られました。

- DMA転送中の時は、当該ブロックの削除を転送が完了するまで待つ。
- infinibandやvideoドライバのような、アプリケーションのメモリ領域に直接データをDMA転送するようなデバイスの場合、いつまでDMAが続くのかがわからない[^4]ので、get_user_pages_longterm()を呼び出すようにし、Filesystem DAXを使えないようにする。

しかし、この解決方法はworkaround的ともいえます。本当に問題を解決するには、デバイスとDAXのマッピング間で調停を行う必要があるのです。

さらに頭の痛いことに、DMAについては（DAXに限らずそもそも一般的な問題として）、ファイルをmmap()でメモリ上に配置した時に、その領域に対してDMA転送でデータが書き込まれてdirtyになっても、filesystemの側がそれを知ることができず、kernelがpanic()するということがLinux Plumbers Conference 2018で指摘されています。

["get_user_pages() + DMA" Linux Plumbers Conference 2018](https://linuxplumbersconf.org/event/2/contributions/126/attachments/136/168/LPC_2018_gup_dma.pdf)

この問題に対しては、put_page()の代わりにput_user_page()というインタフェースを新たに作り[^5]、ドライバ側でput_user_page()を使うべきところを全てそれに書き換えることで、ページカウントの処理などについて矛盾が起きないようにして、問題を解決しようという動きが始まっています。
[[PATCH 0/2] put_user_page*(): start converting the call sites](https://lkml.org/lkml/2018/12/3/1127)

Filesystem DAXではこの問題が解決したうえで、デバイスドライバとDAX間の調停を行う必要が出るでしょう。

#ACPI/_DSM Spec動向

ACPIのNVDIMM Deviceに対する_DSMメソッドはver1.8までアップデートされました。
ただ、specの名前がver1.7の時は[NVDIMM DSM Interface](http://pmem.io/documents/NVDIMM_DSM_Interface-V1.7.pdf)だったのが、ver1.8になって["Intel® Optane™ DC Persistent Memory Module (DCPMM) - DSM Interface"]((http://pmem.io/documents/NVDIMM_DSM_Interface-V1.8.pdf))と変わってしまいました。結局このspecは共通化に至らずIntel NVDIMM固有のSpecということになってしまったようです。

Spec自体の大きな機能追加としては、ver1.6と比べるとSecurityの機能が強化されています。NVDIMMを取り外して持ち出され、他のサーバで読み取られるようなことがあると困りますから、passphraseを入れるまでは読み取れないようにするなどの機能が必要なのです。

法人向けのPCではHDDのパスワードをBIOS画面から入力してHDDのデータを保護することができますが、それと同じような機能と言えるでしょう。
この機能に対応するため、ndctlコマンドも機能が追加されています。

#まとめ

NVDIMM関連の話をざっくりと概観しました。
今年は図が無くて文字ばかりになってしまったので、いささかわかりにくかったかもしれません。申し訳ないです。
個人的にはIntelのNVDIMMのハード的な性能面についてはかなり期待できると思っています。2019年の本格出荷を楽しみにしています。

では。


[^1]: 従来のclflush命令は事実上cacheのflushが終わるまで逐一待つ命令だったのに対し、clflushoptは待たずに複数のcache lineをflushすることができます。またclwbは、これまでflushしたCPU Cacheをinvalidateしていたものを、cacheの再利用に備えてinvalidateしないようにした命令です。
[^2]: 実は、2年前のAdvent Calendarを書いた時点ではすでにこの問題に気が付いていたんですけどね。メンテナーであるDan Williamsにこれついて初めて質問した時、「Fujitsuのエンジニアはいつも難しいこと質問してくるんだよね！」とお言葉を頂戴しました。もっとも、指摘してから機能を開発してUpstreamにマージされるまではなかなか苦労しました。
[^3]: btrfsなどと異なり、ファイルのメタデータのCoWは行いません。ファイルのデータのみです。
[^4]: ずっと録画し続けるようなアプリとデバイスを考えるとわかりやすいでしょう。
[^5]: get_page()とget_user_page()があるのに、put_page()があってなぜかput_user_page()は今まで無かったというのは、ちょっと驚きました。