執筆者 : 西村 大助
1. はじめに
1.1 この記事について
本稿では、Intel Optane Persistent Memory (以下、「DCPMM」1) を使うにあたり必要な、基本的な概念や Linux 上での使用方法などを説明したいと思います。
私自身、Linux の DCPMM 関連の開発を行っているわけではなく、たまたま DCPMM を入手できる機会があったため、その際に調べた事や実機で動かしてみた際に気付いた事などをまとめたものなので、気軽に読んでいただければと思います。
また、現時点で既に第2世代となる 200 シリーズが存在しますが、本稿では第1世代である 100 シリーズを前提としています。
1.2 使用した環境
- HW
- M/B: Supermicro X11SPM-F
- CPU: Intel Xeon Gold 6208U
- DRAM: 16GB DDR4 PC4-21300 (2666MHz) x4
- DCPMM: Intel Optane Persistent Memory(128GB) x2
- SW バージョン
- OS: Ubuntu 20.04.2 LTS(5.8.0-55-generic)
- ipmctl: 02.00.00.3709
- ndctl: 67+
2. Intel Optane Persistent Memory (DCPMM) とは
2.1 概要
DCPMM とは、Intel から発売されている 3D-Xpoint 技術を使った NVDIMM (不揮発性メモリ) の 1 つです。
ざっくり言うと、以下のような特徴があります2。
- DRAM 同様、DIMM slot に挿入して使用する。
- 電源断によりデータが失われない。
- 性能は DRAM には劣るが、SSD などのストレージよりは優れている。
- DRAM に比べ、容量当たりの単価が安く大容量化しやすい。
また、DIMM slot に挿入して使用することからも判る通り、CPU からはメモリとして扱える側面もあるため、使用するには CPU はもちろん、M/B も DCPMM に対応している必要があります。
具体的には、CPU は Xeon Scalable Processor 2nd gen3 の Gold4 以上が必要で、M/B については、例えば Supermicro ではドキュメントに記載されていますが、対応する M/B だけでなく、併用する DRAM との組み合わせや挿入可能な DIMM slot まで細かく規定されています。
2.2 各種概念
本節では、DCPMM を使用するにあたり、必要な概念について説明します。
2.2.1 mode
DCPMM には 2 つ5の動作モード (memory mode と app direct mode) があり、それぞれ以下のような特徴があります。
memory mode (MM)
- DCPMM をシステムメモリとして利用するモード (DRAM は DCPMM のキャッシュとして動作し、ソフトウェアからは見えない)。
- ソフトウェア (OS やアプリケーション) からは通常のメモリとして見えるため、ソフトウェアの対応は不要。
- 揮発性
app direct mode (AD)
- DCPMM をソフトウェア (OS やアプリケーション) に特殊なブロックデバイスとして見せ、ソフトウェア自身に制御させるモード (DRAM は通常通りシステムメモリとして動作)。
- 当然、ソフトウェアの対応が必要。
- 不揮発性
動作モードは後述する region 単位で設定します6。
また、後述する region や namespace といった概念は、app direct mode の時に使われるものです (memory mode では DCPMM は通常のメモリとして扱われるため)。
2.2.2 region
- 1 つ以上の DCPMM をグループ化したもの。
- 異なる CPU ソケット間の DCPMM はグループ化できない。
2.2.3 namespace
- region を複数の領域に分割するためのもので、NVMe における namespace や、ディスクにおけるパーティションのようなもの。
- namespace を作成することで、ソフトウェアからアクセスできるデバイスファイルが作成される (e.g. /dev/pmemX, /dev/daxX)。
- namespace 単位でもアクセスモード (ソフトウェアからどのようにアクセスできるか) の指定が必要。
- sector: 従来のストレージのように扱える。
- fsdax(default): ファイルシステム(対応しているのは xfs or ext4)のファイルを mmap(2) することで DAX (DirectAccess: カーネルのページキャッシュの機構を使わずダイレクトにアクセスすること) が可能となり、DCPMM へダイレクトにマッピングされる。
- devdax: ファイルシステムを介さずに直接 DAX が可能となる。
3. 管理ツール
前章で各種概念について説明しましたが、これらの設定は、ipmctl
や ndctl
といったツールで行います。
具体的には、region の作成と DCPMM の動作モード (MM/AD) の設定は ipmctl
、namespace の設定は ndctl
で行います7。
それぞれのツールの詳細については、下記マニュアルを参照してください。
次章では実機でそれらのツールを使って設定を行ってみたいと思います。
4. 実機確認
4.1. 状況確認
そもそも OS からはどのように認識されているのか確認するため、dmesg
や /proc/iomem
を見てみましょう。
# dmesg | grep -i -C2 persistent [ 0.000000] BIOS-e820: [mem 0x00000000ff000000-0x00000000ffffffff] reserved [ 0.000000] BIOS-e820: [mem 0x0000000100000000-0x000000107fffffff] usable [ 0.000000] BIOS-e820: [mem 0x0000001080000000-0x0000004f7fffffff] persistent (type 7) [ 0.000000] BIOS-e820: [mem 0x0000004f80000000-0x0000004f8fffffff] reserved [ 0.000000] NX (Execute Disable) protection: active ... # grep -i -C2 persistent /proc/iomem e4f600000-e4f87a7bf : Kernel data e4fb34000-e4fffffff : Kernel bss 1080000000-4f7fffffff : Persistent Memory 4f80000000-4f8fffffff : Reserved 4f8fffe000-4f8fffefff : ndbus0
すなわち、1080000000-4f7fffffff が persistent memory と認識されていることがわかります。
では、ipmctl
を使って状態を確認してみましょう。
# ipmctl show -topology DimmID | MemoryType | Capacity | PhysicalID| DeviceLocator ================================================================================ 0x0020 | Logical Non-Volatile Device | 126.375 GiB | 0x002c | DIMMC1 0x0120 | Logical Non-Volatile Device | 126.375 GiB | 0x0034 | DIMMF1 N/A | DDR4 | 16.000 GiB | 0x0028 | DIMMA1 N/A | DDR4 | 16.000 GiB | 0x002a | DIMMB1 N/A | DDR4 | 16.000 GiB | 0x0030 | DIMMD1 N/A | DDR4 | 16.000 GiB | 0x0032 | DIMME1
DIMM C1/F1 に Non-Volatile Device (DCPMM) が認識されていることがわかります。
また、以下の通り、DCPMM は全て app direct mode となっていることもわかります。
# ipmctl show -memoryresources MemoryType | DDR | DCPMM | Total ======================================================= Volatile | 64.000 GiB | 0.000 GiB | 64.000 GiB AppDirect | - | 252.000 GiB | 252.000 GiB Cache | 0.000 GiB | - | 0.000 GiB Inaccessible | - | 0.844 GiB | 0.844 GiB Physical | 64.000 GiB | 252.844 GiB | 316.844 GiB
region は、ipmctl
, ndctl
どちらでも確認できます。
# ipmctl show -region SocketID | ISetID | PersistentMemoryType | Capacity | FreeCapacity | HealthState ================================================================================================= 0x0000 | 0x3feeeeb82d632444 | AppDirect | 252.000 GiB | 252.000 GiB | Healthy # ndctl list -R [ { "dev":"region0", "size":270582939648, "available_size":270582939648, "max_available_extent":270582939648, "type":"pmem", "iset_id":4606881943649461316, "persistence_domain":"memory_controller" } ]
iset_id: 0x3feeeeb82d632444 = 4606881943649461316 の region が 1 つ作成されていることがわかります。
namespace は ndctl
で確認できますが、
# ndctl list -N (表示なし)
現時点では、特に作成されていません。
4.2 memory mode への変更
では、memory mode への変更を行ってみたいと思います。
region の設定は ipmctl
を使用しますが、設定を行うには設定内容を goal として作成し、システムを再起動します (再起動により、goal が反映されて、goal は削除されます)。
goal の作成には、ipmctl create [OPTIONS] -goal [PROPERTIES]
を実行します。
ここでは 2 枚の DCPMM を memory mode の region とするため、以下のように実行します。
# ipmctl create -dimm 0x0020,0x0120 -goal MemoryMode=100 WARNING! The requested memory mode size for 2LM goal is below the recommended NM:FM limit of 1:4 The following configuration will be applied: SocketID | DimmID | MemorySize | AppDirect1Size | AppDirect2Size =================================================================== 0x0000 | 0x0020 | 126.000 GiB | 0.000 GiB | 0.000 GiB 0x0000 | 0x0120 | 126.000 GiB | 0.000 GiB | 0.000 GiB Do you want to continue? [y/n] y Created following region configuration goal SocketID | DimmID | MemorySize | AppDirect1Size | AppDirect2Size =================================================================== 0x0000 | 0x0020 | 126.000 GiB | 0.000 GiB | 0.000 GiB 0x0000 | 0x0120 | 126.000 GiB | 0.000 GiB | 0.000 GiB A reboot is required to process new memory allocation goals.
MemoryMode=XX で memory mode とする割合を指定しますが、ここでは 100% としています。
作成された goal は、以下のコマンドで確認できます。
# ipmctl show -goal SocketID | DimmID | MemorySize | AppDirect1Size | AppDirect2Size =================================================================== 0x0000 | 0x0020 | 126.000 GiB | 0.000 GiB | 0.000 GiB 0x0000 | 0x0120 | 126.000 GiB | 0.000 GiB | 0.000 GiB A reboot is required to process new memory allocation goals.
システムを再起動後、確認すると、以下のような感じなり、DCPMM が volatile なシステムメモリとなっていることがわかります。
# ipmctl show -region There are no Regions defined in the system. # ipmctl show -memoryresources MemoryType | DDR | DCPMM | Total ======================================================= Volatile | 0.000 GiB | 252.000 GiB | 252.000 GiB AppDirect | - | 0.000 GiB | 0.000 GiB Cache | 64.000 GiB | - | 64.000 GiB Inaccessible | - | 0.844 GiB | 0.844 GiB Physical | 64.000 GiB | 252.844 GiB | 316.844 GiB
また、この状態では、前述の app direct mode の場合と違い、/proc/iomem
にも persistent memory は表示されず、そのことからも DCPMM がシステムメモリとして見えていることがわかります。
# grep -i -C2 persistent /proc/iomem (表示なし)
4.3 app direct mode への変更
上記とほぼ同様の手順で、app direct mode に戻してみましょう。
# ipmctl create -dimm 0x0020,0x0120 -goal MemoryMode=0 The following configuration will be applied: SocketID | DimmID | MemorySize | AppDirect1Size | AppDirect2Size ================================================================== 0x0000 | 0x0020 | 0.000 GiB | 126.000 GiB | 0.000 GiB 0x0000 | 0x0120 | 0.000 GiB | 126.000 GiB | 0.000 GiB Do you want to continue? [y/n] y Created following region configuration goal SocketID | DimmID | MemorySize | AppDirect1Size | AppDirect2Size ================================================================== 0x0000 | 0x0020 | 0.000 GiB | 126.000 GiB | 0.000 GiB 0x0000 | 0x0120 | 0.000 GiB | 126.000 GiB | 0.000 GiB A reboot is required to process new memory allocation goals. # ipmctl show -goal SocketID | DimmID | MemorySize | AppDirect1Size | AppDirect2Size ================================================================== 0x0000 | 0x0020 | 0.000 GiB | 126.000 GiB | 0.000 GiB 0x0000 | 0x0120 | 0.000 GiB | 126.000 GiB | 0.000 GiB A reboot is required to process new memory allocation goals.
システムを再起動後、確認すると、DCPMM は app direct mode に戻っていることがわかります。
# ipmctl show -memoryresources MemoryType | DDR | DCPMM | Total ======================================================= Volatile | 64.000 GiB | 0.000 GiB | 64.000 GiB AppDirect | - | 252.000 GiB | 252.000 GiB Cache | 0.000 GiB | - | 0.000 GiB Inaccessible | - | 0.844 GiB | 0.844 GiB Physical | 64.000 GiB | 252.844 GiB | 316.844 GiB
4.4 namespace の作成
次に、namespace を作成してみましょう。
name space の作成には、ndctl create-namespace [OPTIONS]
を実行します。
主なオプションには、
- -m
: アクセスモードの指定 - -r
: 対象となる region の指定 - -s
: namespace のサイズ
などがあります。
では、実際に、sector/fsdax/devdax のそれぞれの namespace を作成してみましょう。
# ndctl create-namespace -r region0 -m sector -s 32G { "dev":"namespace0.0", "mode":"sector", "size":"31.97 GiB (34.33 GB)", "uuid":"44ec528a-2973-4a64-b499-9b8545e0449e", "sector_size":4096, "blockdev":"pmem0s" } root@aries:~/DCPMM# ndctl create-namespace -r region0 -m fsdax -s 32G { "dev":"namespace0.1", "mode":"fsdax", "map":"dev", "size":"31.50 GiB (33.82 GB)", "uuid":"b98c5ed4-52cc-4df7-93c7-8c7516d445ea", "sector_size":512, "align":2097152, "blockdev":"pmem0.1" } root@aries:~/DCPMM# ndctl create-namespace -r region0 -m devdax -s 32G { "dev":"namespace0.2", "mode":"devdax", "map":"dev", "size":"31.50 GiB (33.82 GB)", "uuid":"9c9a02b9-d43a-4373-ac59-d952fae9e2f0", "daxregion":{ "id":0, "size":"31.50 GiB (33.82 GB)", "align":2097152, "devices":[ { "chardev":"dax0.2", "size":"31.50 GiB (33.82 GB)", "target_node":1, "mode":"devdax" } ] }, "align":2097152 }
と作成できました。
作成した namespace は、以下のコマンドで確認できます。
# ndctl list -N [ { "dev":"namespace0.2", "mode":"devdax", "map":"dev", "size":33820770304, "uuid":"9c9a02b9-d43a-4373-ac59-d952fae9e2f0", "chardev":"dax0.2", "align":2097152 }, { "dev":"namespace0.1", "mode":"fsdax", "map":"dev", "size":33820770304, "uuid":"b98c5ed4-52cc-4df7-93c7-8c7516d445ea", "sector_size":512, "align":2097152, "blockdev":"pmem0.1" }, { "dev":"namespace0.0", "mode":"sector", "size":34325135360, "uuid":"44ec528a-2973-4a64-b499-9b8545e0449e", "sector_size":4096, "blockdev":"pmem0s" } ]
また、作成時のメッセージや ndctl list -N
の出力を見るとわかる通り、sector/fsdax/devdax それぞれについて /dev/pmem0s
//dev/pmem0.1
//dev/dax0.2
というデバイスファイルが作成されていることもわかります。
# ls -l /dev/pmem0s /dev/pmem0.1 /dev/dax0.2 crw------- 1 root root 250, 3 9月 7 21:56 /dev/dax0.2 brw-rw---- 1 root disk 259, 6 9月 7 21:56 /dev/pmem0.1 brw-rw---- 1 root disk 259, 5 9月 7 21:55 /dev/pmem0s
5. 最後に
「使ってみた」と表題にあげた通り、 namespace の各モードで性能を比較してみたり、PMDK を使ってみたりしたかったのですが、
時間の都合上、基本的な説明と設定だけで終わってしまいました。
個人的には、カーネルの中の実装も気になっているので、性能評価や実装の調査などもまとまったら記事にしたいと思います。