nvmeコマンド逆引きマニュアル(2/2)

# はじめに
　[前回の記事](https://qiita.com/ken-yossy/items/7d20929ce2de0edd2190)に引き続き、NVMe SSDに対してLinux用のNVMe SSD操作ツール（コマンド）を使用して所望の操作を行う方法、いわゆる「逆引きマニュアル」をまとめます。

# 記載する内容
* この記事に記載している内容
    * 自己診断実行
    * フォーマット実行
    * Trim実行
    * 揮発ライトキャッシュの無効化／有効化
    * SSDが備える低消費電力状態を調べる
    * 低消費電力状態への自動遷移
    * 低消費電力状態への自動遷移の無効化／有効化
    * 廃棄や譲渡を前提としたデータの難読化
    * Host Memory Buffer (HMB)の有効無効切り替え
* [前回の記事](https://qiita.com/ken-yossy/items/7d20929ce2de0edd2190)に記載した内容
    * 個体識別情報取得
    * 容量取得
    * 温度取得
    * 残寿命取得
    * サポートコマンドリスト取得
    * サーマルスロットリング関連温度の取得
    * サーマルスロットリング関連温度の設定
    * サーマルスロットリングの無効化と有効化

# 準備（再掲）
　ここでは[``nvme-cli``パッケージ](https://github.com/linux-nvme/nvme-cli)をLinux (Ubuntu)上で使用することを前提とします。また、操作対象のNVMe SSDは``/dev/nvme0``とします。お使いの環境に合わせて適宜読みかえてください。

　動作確認したOSはUbuntu 20.04.6 LTS、カーネルは5.4.0-173 (amd64)、``nvme-cli``パッケージのバージョンは1.9.1ubuntu0.1です。

　なお、操作の際にはデバイスを読み書きしますので例えば``sudo``コマンドなどで適切な権限のもと実行できるようにしておく必要があります。

:::note warn
設定変更を伴う操作は、ドライブの挙動が不安定になる、記録されていたデータが読み出せなくなる、などの副作用を伴う可能性があります。操作の内容を良く理解し、十分に注意して実行してください。
:::
 
# 自己診断実行
　NVMe仕様における「自己診断」機能は``Device Self-test``と呼ばれます。この機能はオプションですので、SSDがサポートしているかどうかを調べる必要があります。
```
% sudo nvme effects-log /dev/nvme0 -H | grep Device
ACS20    [Device Self-test                ] 00000001  CSUPP+  LBCC-  NCC-  NIC-  CCC-  USS-  No command restriction
```
　このように表示されれば``Device Self-test``コマンドをサポートしています。

　``Device Self-test``コマンドで実行できる自己診断には、短時間で終わる診断と長時間かかる診断の2つがあります。NVMe仕様は短時間の診断は2分以下で終わることを推奨しています。長時間の診断については、所要時間をコントローラ情報で示すこととされています。
```
% sudo nvme id-ctrl /dev/nvme0 -H | grep edstt
edstt     : 10
```
　長時間診断の目安時間はこのExtended Device Self-test Time (EDSTT)というフィールドで示されます。このフィールドの値は分単位なので、このSSDの長時間診断は目安として10分かかることがわかります。

　それでは長時間診断を実行してみます。``Device Self-test``コマンドの発行は``device-self-test``サブコマンドで行います。
```
% sudo nvme device-self-test /dev/nvme0 -s 2
Device self-test started
```
　実行開始する診断の種類は``-s``オプションで指定します。1が短時間の診断で2が長時間の診断です。ちなみに0xFを指定すると実行中の診断の中止を要求できます。

　実行中の自己診断の様子（進行度合い）は``self-test-log``サブコマンドで取得可能です。実行中の場合はこのように表示されます。
```
% sudo nvme self-test-log /dev/nvme0
Test is 13% complete and is still in progress.
```
　実行が完了しているとこのように表示されます。
```
% sudo nvme self-test-log /dev/nvme0
Test is 0% complete and is still in progress.
```
　本当は実行時刻やエラーの有無など詳細な情報が取得できているはずで、それを表示して欲しいのですが、どうやら表示しないようです。ただ、実行は終了していると思われます。

　こんな時は生データを取得して内容を確認します。生の自己診断結果データは``get-log``サブコマンドで取得可能です。自己診断結果を示すログ番号は6ですので、以下のようにして取得します。
 
　なお、自己診断結果のログには最近20回分の診断結果が含まれていて合計564バイトなのですが、一番最近の結果だけであれば先頭32バイトに収まりますのでここでは先頭32バイトだけ取得します。
```
% sudo nvme get-log /dev/nvme0 -i 6 -l 32
Device:nvme0 log-id:6 namespace-id:0xffffffff
       0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
0000: 00 00 00 00 20 00 00 00 2c 00 00 00 00 00 00 00 "........,......."
0010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 "................"
```
　NVMe仕様を片手にこのデータを解読すると以下のようになります。バイト0と1が自己診断全体に関する情報で、バイト4から31までが一番最近実行した自己診断の結果です。

| バイト | ビット | 値 | 意味 | 備考 |
|:-:|:-:|:-:|:-:|:-:|
|  0 | 3:0  | 0  | 診断中ではない  | |
|  1 | 6:0  | 0  | （無視）  | 診断中は診断の完了率(%) |
|  3:2 |      | 0  | （予約）  |
|  4 | 7:4  | 2  | 長時間診断 | 実行した診断の種類 |
|    | 3:0  | 0  | エラーなく完了 | |
|  5 |      | 0  | （無視）| エラー検出時はテスト識別子 |
|  6 |      | 0  | （無視）| エラー検出時はエラー情報 |
|  7 |      | 0  |（予約）| |
|  15:8 |   | 0x2c (44) | 診断完了時刻 | ドライブ稼働時間(POH)基準で単位は時間(hours) | 
| 31:16 |   | 0  | （無視）| エラー検出時はエラー情報 |

# フォーマット実行
　NVMe仕様においてフォーマット処理を実行するコマンドは``Format NVM``コマンドです。

::: note warn
NVMe仕様におけるフォーマットとは、「セクタサイズを変更する」とか「メタデータの種類を変更する」などの処理を指します。「データを読めなくする」ことが目的ではありません（実際に読めなくなるかどうかも不定です）。
:::

　このコマンドはオプションですのでSSDがこのコマンドをサポートしているかどうかを調べる必要があります。
```
% sudo nvme effects-log -H /dev/nvme0 | grep Format
ACS128   [Format NVM                      ] 00020003  CSUPP+  LBCC+  NCC-  NIC-  CCC-  USS-  No other command for any namespace
```
　このSSDは``Format NVM``コマンドをサポートしてますので、実際に発行してみます。今このSSDはセクタサイズが4,096バイト(4 KB)なので、これを512バイトに変更してみます。

　``Format NVM``コマンド発行には``format``サブコマンドを使用します。またフォーマットはNamespaceに対して実行するものですので、``-n``オプションを使用してNamespaceを指定するか``/dev/nvme0n1``のようにNamespaceを含んだデバイスファイルを指定します。
```
% sudo nvme id-ns -H /dev/nvme0 -n 1
(snip)
LBA Format  0 : Metadata Size: 0   bytes - Data Size: 512 bytes - Relative Performance: 0x2 Good
LBA Format  1 : Metadata Size: 0   bytes - Data Size: 4096 bytes - Relative Performance: 0x1 Better (in use)

% sudo nvme format /dev/nvme0 -n 1 -b 512
Success formatting namespace:1

% sudo nvme id-ns -H /dev/nvme0 -n 1
(snip)
LBA Format  0 : Metadata Size: 0   bytes - Data Size: 512 bytes - Relative Performance: 0x2 Good (in use)
LBA Format  1 : Metadata Size: 0   bytes - Data Size: 4096 bytes - Relative Performance: 0x1 Better
```
　フォーマットにより無事セクタサイズを変更できました。
 
# Trim実行
　NVMe仕様におけるいわゆる「トリム(Trim)」は``Dataset Management``コマンドで発行します。このため、まずはSSDが``Dataset Management``コマンドに対応していることを確認します。
```
% sudo nvme effects-log -H /dev/nvme0 | grep Dataset
IOCS9    [Dataset Management              ] 00000003  CSUPP+  LBCC+  NCC-  NIC-  CCC-  USS-  No command restriction
```
　このSSDは``Dataset Management``コマンドをサポートしていますので、実際にコマンドを発行してみます。

::: note warn
Trimを実行すると対象セクタのデータを失いますので十分に注意してください。
:::

　nvmeコマンドで``Dataset Management``コマンドを発行するには``dsm``サブコマンドを使用します。ここでは試しにLBA 0をTrimしてみます。

　このSSDは下記の通りTrimして未書き込み状態のセクタからは0が読めるようですので、LBA 0にランダムデータをWriteしてTrimし、その後未書き込みの状態でLBA 0をReadして0が読めることを確認します。
```
% sudo nvme id-ns -H /dev/nvme0n1
(snip)
  [2:0] : 0x1   Bytes Read From a Deallocated Logical Block and its Metadata are 0x00
```
　まずLBA = 0に``/dev/random``から読んで作成したランダムデータを書きます。このSSDは上でフォーマットしたSSDですので現在セクタサイズは512バイトです。
```
% sudo nvme write /dev/nvme0n1 -s 0 -c 0 -z 512 -d test.dat
write: Success

% sudo nvme read /dev/nvme0n1 -s 0 -c 0 -z 512 | hexdump
read: Success
0000000 1489 5dbf 21e7 eca2 75e2 1a8d 1f2a 268b
(snip)
00001f0 e586 3bb0 0982 7165 9e27 9f1c a747 9e63
0000200
```
　``-s 0``がLBA 0を、``-c 0``が1セクタであることを示します（0-based valueであることに注意）。また``-z 512``で転送データサイズを指定しています。

　そしてLBA = 0をTrimして、直後にLBA 0をReadして0が読めることを確認します。
```
% sudo nvme dsm /dev/nvme0n1 -d -s 0 -b 1
NVMe DSM: success

% sudo nvme read /dev/nvme0n1 -s 0 -c 0 -z 512 | hexdump
read: Success
0000000 0000 0000 0000 0000 0000 0000 0000 0000
*
0000200
```
　この通りTrimに成功してTrimしたセクタ(LBA = 0)から0が読めることを確認できました。

　``dsm``サブコマンドの引数のうち、``-d``は操作がTrim (Deallocate)であること、``-s 0``はTrim対象の先頭LBAが0であること、そして``-b 1``はTrimするセクタ数が1であることを指定しています。

　これを応用すれば、ドライブ全体をTrimするには上記``dsm``サブコマンドの引数に``-s 0 -b <ドライブのセクタ数>``を指定すれば良いことがわかります。

# 揮発ライトキャッシュの無効化／有効化
　SSDが揮発ライトキャッシュ(Volatile Write Cache: VWC)を備えるかどうかはコントローラ情報にて判別できます。
```
% sudo nvme id-ctrl /dev/nvme0 | grep vwc
vwc       : 0x1
```
　このvwcフィールドが1であれば揮発ライトキャッシュをサポートしています。

　揮発ライトキャッシュの現在の設定は``get-feature``サブコマンドで以下のように調べます。揮発ライトキャッシュの機能IDは6です。
```
% sudo nvme get-feature /dev/nvme0 -f 0x6
get-feature:0x6 (Volatile Write Cache), Current value:0x000001
```
　取得した値が1であれば有効という意味です。この設定が変更可能かどうかも``get-feature``サブコマンドで調べます。
```
% sudo nvme get-feature /dev/nvme0 -f 0x6 -s 3
get-feature:0x6 (Volatile Write Cache), Supported capabilities value:0x000004
  Feature is changeable
```
　このSSDの場合、設定は変更可能ですが不揮発化はできないようです(not saveable)。つまり、設定を変更して無効化しても電源を入れ直すと元に戻ります（有効になる）。

　設定変更には``set-feature``サブコマンドを使用します。無効にする場合は0を、有効にする場合は1を指定します。下記は無効化した例です。
```
% sudo nvme set-feature /dev/nvme0 -f 0x6 -v 0
set-feature:06 (Volatile Write Cache), value:00000000
% sudo nvme get-feature /dev/nvme0 -f 0x6
get-feature:0x6 (Volatile Write Cache), Current value:00000000
```
　``set-feature``サブコマンドで0を指定して無効化し、``get-feature``サブコマンドで実際に無効化されたことが確認できました。

　なお、変更時に不揮発化する場合は``set-feature``サブコマンドに``--save``オプション（短縮形は``-s``）を付与する必要があります。

# SSDが備える低消費電力状態を調べる
　これはコントローラ情報の末尾に表示されます。
```
% sudo nvme id-ctrl -H /dev/nvme0
(snip)
ps    0 : mp:6.37W operational enlat:1 exlat:1 rrt:0 rrl:0
          rwt:0 rwl:0 idle_power:- active_power:-
ps    1 : mp:5.52W operational enlat:1 exlat:1 rrt:1 rrl:1
          rwt:1 rwl:1 idle_power:- active_power:-
ps    2 : mp:5.09W operational enlat:1 exlat:1 rrt:2 rrl:2
          rwt:2 rwl:2 idle_power:- active_power:-
ps    3 : mp:0.0500W non-operational enlat:7000 exlat:5000 rrt:3 rrl:3
          rwt:3 rwl:3 idle_power:- active_power:-
ps    4 : mp:0.0050W non-operational enlat:13000 exlat:36000 rrt:4 rrl:4
          rwt:4 rwl:4 idle_power:- active_power:-
```
　この結果からわかる重要なことは、このSSDが通常状態であるPower State 0 (PS0)に加えて4つの低消費電力状態（PS1からPS4）をサポートし、うちPS3とPS4がNon-operational Power StateつまりReadやWriteなどのデータアクセスコマンドを実行できない状態であることです。
 
　Non-operational Power Stateはいわゆる「スリープ状態」を指し、SSDがこの状態の時にホストからReadやWriteなどのデータアクセスコマンドを受信した際はOperational Power State、このSSDで言えばPS0からPS2までのいずれかのPower Stateに遷移してから処理を行う必要があります。

　このため、上記状態遷移が必要な際はコマンド処理に状態遷移のレイテンシが上乗せされることになり、若干応答が悪化します。

# 低消費電力状態への自動遷移
　NVMeにはSSDが自身の判断で低消費電力状態に遷移するAutonomous Power State Transition (APST)という機能があります。SSDがこの機能をサポートしているかどうかはコントローラ情報にて判別できます。
```
% sudo nvme id-ctrl /dev/nvme0 | grep apsta
apsta     : 0x1
```
　このapstaフィールドが1であればAPSTをサポートしています。

　APSTの現在の設定は``get-feature``サブコマンドで以下のように調べます。APSTの機能IDは0xC (12)です。
```
% sudo nvme get-feature /dev/nvme0 -H -f 0xc
get-feature:0xc (Autonomous Power State Transition), Current value:0x000001
        Autonomous Power State Transition Enable (APSTE): Enabled
        Auto PST Entries        .................
        Entry[ 0]
        .................
        Idle Time Prior to Transition (ITPT): 600 ms
        Idle Transition Power State   (ITPS): 3
        .................
        Entry[ 1]
        .................
        Idle Time Prior to Transition (ITPT): 600 ms
        Idle Transition Power State   (ITPS): 3
        .................
        Entry[ 2]
        .................
        Idle Time Prior to Transition (ITPT): 600 ms
        Idle Transition Power State   (ITPS): 3
        .................
        Entry[ 3]
        .................
        Idle Time Prior to Transition (ITPT): 2450 ms
        Idle Transition Power State   (ITPS): 4
        .................
        Entry[ 4]
        .................
        Idle Time Prior to Transition (ITPT): 0 ms
        Idle Transition Power State   (ITPS): 0
```
　この結果からはまずAPSTの現在の設定は有効であることがわかります。それに続く内容は遷移条件を示していて、上記の結果は以下のような内容です。

| 遷移元Power State | 遷移条件（アイドル継続時間） | 遷移先Power State |
|:-:|:-:|:-:|
| 0  | 600ミリ秒  | 3  |
| 1  | 600ミリ秒  | 3  |
| 2  | 600ミリ秒 | 3 |
| 3  | 2,450ミリ秒 | 4 |

　例えば最初の行は「Power Stateが0の時に600ミリ秒アイドル状態が続いたことを検出したらPower State 3に遷移する」という意味です。

　なお遷移先Power StateにPower State 1と2が登場していませんが、これはNVMe仕様において遷移先Power Stateに指定できるのは「Non-operational Power Stateのみ」と規定されているからです。

　このSSDは上記低消費電力状態を調べたSSDと同じですので、PS3とPS4がNon-operational Power Stateです。このため上記「遷移先Power State」にはPS3とPS4しか登場しない、ということになります。

# 低消費電力状態への自動遷移の無効化／有効化
　この低消費電力状態への自動遷移の無効と有効も切り替えること可能です。

　まずはこの設定が変更可能かどうかを``get-feature``サブコマンドで調べます。
```
% sudo nvme get-feature /dev/nvme0 -f 0xc -s 3
get-feature:0xc (Autonomous Power State Transition), Supported capabilities value:0x000005
  Feature is saveable
  Feature is changeable
```
　この結果によれば、このSSDのAPSTの設定は変更可能でかつ不揮発化可能です。

　NVMe仕様によれば、APSTを無効にするには0を指定して``set-feature``サブコマンドを発行すれば良い（他にデータを転送する必要はない）ように読めるのですが、各低消費電力状態への遷移条件などのデータも同時に渡す必要があるようです。

　そこで、後で元の設定へ戻すために一旦現在の設定内容を保存してから無効化してみます。``get-feature``サブコマンドに``-b``オプションをつけることで出力内容をバイナリデータで取得できます。
```
% sudo nvme get-feature /dev/nvme0 -f 0xc -b > apst.dat
% hexdump apst.dat
0000000 e918 0002 0000 0000 e918 0002 0000 0000
0000010 e918 0002 0000 0000 9820 0008 0000 0000
0000020 0000 0000 0000 0000 0000 0000 0000 0000
*
0000100
```
　これでこのSSDの設定値を取得できました。データのサイズは256バイト(0x100)です。

　では、256バイトのオールゼロのデータを渡して低消費電力状態への自動遷移を無効化してみます。
```
% dd bs=256 count=1 if=/dev/zero | sudo nvme set-feature /dev/nvme0 -f 0xc -v 0 -l 256
1+0 records in
1+0 records out
256 bytes copied, 9.5612e-05 s, 2.7 MB/s
set-feature:0c (Autonomous Power State Transition), value:00000000
       0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
0000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 "................"
0010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 "................"
0020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 "................"
0030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 "................"
0040: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 "................"
0050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 "................"
0060: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 "................"
0070: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 "................"
0080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 "................"
0090: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 "................"
00a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 "................"
00b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 "................"
00c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 "................"
00d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 "................"
00e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 "................"
00f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 "................"
```
　これで無効化完了です。再度``get-feature``サブコマンドでこの機能の状態を取得するとvalueが0であることを確認できます。

　最後に、先ほど保存しておいたデータを使用して設定内容を元に戻します（有効化します）。
```
% cat apst.dat | sudo nvme set-feature /dev/nvme0 -f 0xc -v 1 -l 256
set-feature:0c (Autonomous Power State Transition), value:0x000001
       0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
0000: 18 e9 02 00 00 00 00 00 18 e9 02 00 00 00 00 00 "................"
0010: 18 e9 02 00 00 00 00 00 20 98 08 00 00 00 00 00 "................"
0020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 "................"
0030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 "................"
0040: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 "................"
0050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 "................"
0060: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 "................"
0070: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 "................"
0080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 "................"
0090: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 "................"
00a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 "................"
00b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 "................"
00c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 "................"
00d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 "................"
00e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 "................"
00f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 "................"
```
　これでこの機能の設定を元に戻すことができました。
 
# 廃棄や譲渡を前提としたデータの難読化
　この目的には``Format NVM``コマンドではなく``Sanitize``コマンドを使用すべきなので、ここでは``Sanitize``コマンドの発行方法を示します。

　まず、対象のSSDが``Sanitize``コマンドをサポートしているかどうかを調べます。
 ```
 % sudo nvme effects-log -H /dev/nvme0 | grep Sanitize
 CS132   [Sanitize                        ] 00000003  CSUPP+  LBCC+  NCC-  NIC-  CCC-  USS-  No command restriction
 ```
　こんな形で表示されればサポートしています。
 
　NVMe仕様ではSanitizeコマンドに3つの動作を規定しています。それは、上書き、ブロック消去、そして暗号的消去の3つです。そこで、次は操作しようとしているSSDがこの3つのうちどれをサポートしているかを調べます。
```
% sudo nvme id-ctrl -H /dev/nvme0
(snip)
    [2:2] : 0   Overwrite Sanitize Operation Not Supported
    [1:1] : 0x1 Block Erase Sanitize Operation Supported
    [0:0] : 0   Crypto Erase Sanitize Operation Not Supported
(snip)
```
　このSSDはブロック消去のみサポートしているようです。では、実際にSanitizeしてみます。Sanitizeされたことを確認するため、LBA 0にランダムデータを書いておき、Sanitize後にLBA 0のデータを読んでみます。

　まずランダムデータを書き込みます。
```
% dd if=/dev/random ibs=512 count=1 | sudo nvme write /dev/nvme0n1 -s 0 -c 0 -z 512
1+0 records in
1+0 records out
512 bytes copied, 0.00010967 s, 4.7 MB/s
Rounding data size to fit block count (4096 bytes)
write: Success

% sudo nvme read /dev/nvme0n1 -s 0 -c 0 -z 512 | hexdump
Rounding data size to fit block count (4096 bytes)
read: Success
0000000 bf8b 6a0e f73a 80b9 9f89 d347 9822 48d8
(snip)
00001f0 27d0 81f5 36ee 4320 8512 8461 d578 1ae6
0000200
```
　LBA 0にランダムデータを書き込みましたので、実際にSanitizeします。

　Sanitizeは``sanitize``サブコマンドを使用します。``sanitize``サブコマンドには``-a``オプションを使用してどの動作をするのか指定する必要があります。ブロック消去なら2、上書きなら3、暗号的消去なら4です。このSSDはブロック消去のみサポートしていますので、ブロック消去を指定して実行します。

```
% sudo nvme sanitize /dev/nvme0 -a 2

% sudo nvme get-log /dev/nvme0 -i 0x81 -l 512
Device:nvme0 log-id:129 namespace-id:0xffffffff
       0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
0000: 99 99 02 00 02 00 00 00 ff ff ff ff 3c 00 00 00 "............<..."
0010: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff "................"
(snip)
01f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 "................"

% sudo nvme get-log /dev/nvme0 -i 0x81 -l 512
Device:nvme0 log-id:129 namespace-id:0xffffffff
       0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
0000: ff ff 01 01 02 00 00 00 ff ff ff ff 3c 00 00 00 "............<..."
0010: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff "................"
(snip)
01f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 "................"
```
　``sanitize``サブコマンドを実行しても何も表示されないので、上記の例では``get-log``サブコマンドを使用してSanitizeの進行状況を取得しています。

　取得結果の3バイト目（バイト2）のビット0と1がSanitize処理をしているかどうかを示します。1回目の取得時は2であり、これはSanitize処理が進行中であることを示します。具体的な進行状況は先頭2バイトに示されます。値は9999h (39,321)ですが、この値は65,536を100%とする進行状況なので39,321÷65,536x100で約60%完了、となります。

　2回目の取得時はバイト2のビット1:0が1であり、これはSanitize処理が完了して成功したことを示します。

　そこでLBA 0を読んでみると、
```
% sudo nvme read /dev/nvme0n1 -s 0 -c 0 -z 512 | hexdump
Rounding data size to fit block count (4096 bytes)
read: Success
0000000 0000 0000 0000 0000 0000 0000 0000 0000
*
0000200
```
　オールゼロが読めました。これでSanitize完了です。

# Host Memory Buffer (HMB)の有効無効切り替え
　最近のDRAMを搭載しないNVMe SSDは、Host Memory Buffer (HMB)という機能を使用してOSからホスト側のDRAMの一部をSSD用に割り当ててもらいそれを使用しているものがあります。Linuxの起動時のメッセージを``dmesg``コマンドで確認すると、HMBによるSSDに対するメモリの割り当てが行われた場合は次のように表示されます。
```
% dmesg | grep nvme
[    0.935501] nvme nvme0: pci function 0000:01:00.0
[    1.153904] nvme nvme0: allocated 32 MiB host memory buffer.
[    1.155148] nvme nvme0: 6/0/0 default/read/poll queues
[    1.159486]  nvme0n1: p1 p2
```
　2行目に、SSDに対して32 MiB割り当てたことが表示されています。

　SSDがHMBに対応しているかどうかはコントローラ情報に表示されています。
```
% sudo nvme id-ctrl /dev/nvme0 | grep ^hm
hmpre     : 51200
hmmin     : 823
hmminds   : 0
hmmaxd    : 8
```
　このhmpreが非ゼロの場合、SSDがHMBをサポートしていることになります。Host Memory Buffer Preferred Size (HMPRE)はSSDが希望するメモリサイズ（4 KB単位）、Host Memory Buffer Minimum Size (HMMIN)はSSDが希望する最小メモリサイズ（4 KB単位）です。

　上記例の場合、SSDは割り当てサイズについて最小で約2 MiB、希望値は4 KB x 51200 = 200 MiBとホストに伝え、その結果32 MiB割り当てられた、ということになります。

　このHMBの設定（割り当てを要求するかどうか）も変更できることがあります。これは``get-feature``サブコマンドで調べることができます。
```
% sudo nvme get-feature /dev/nvme0 -H -f 0xd -s 3
get-feature:0xd (Host Memory Buffer), Supported capabilities value:0x000004
  Feature is changeable
```
　このSSDでは設定変更は可能であるものの設定変更の不揮発化はできず、電源を切り再投入するとHMB有効の状態で起動するようです。

　試しに設定を変更してみた結果は下記の通りです。
```
% sudo nvme set-feature /dev/nvme0 -f 0xd -v 0
set-feature:0d (Host Memory Buffer), value:00000000

% sudo nvme get-feature /dev/nvme0 -H -f 0xd
get-feature:0xd (Host Memory Buffer), Current value:00000000
        Memory Return       (MR): False
        Enable Host Memory (EHM): Disabled
        Host Memory Descriptor List Entry Count (HMDLEC): 0
        Host Memory Descriptor List Address     (HMDLAU): 0x0
        Host Memory Descriptor List Address     (HMDLAL): 0x0
        Host Memory Buffer Size                  (HSIZE): 0
```
　``set-feature``サブコマンドでHMBを無効化し、``get-feature``サブコマンドで設定を確認すると、確かHMBが無効となり割り当てサイズ(HSIZE)がゼロになりました。

# おわりに
　この記事では、前回に引き続き、NVMe SSDに対してLinux用のNVMe SSD操作ツール（コマンド）を使用して所望の操作を行う方法、いわゆる「逆引きマニュアル」をまとめました。

　前回説明した内容と合わせて、NVMe SSDを操作する際の助けになれば幸いです。

　ただし前回の記事でも触れましたが、これらの設定はメーカーが製品の設計や特徴を踏まえて設定しているものが多く、それらを変更することは副作用を伴う可能性が高いです。このため、操作を行う際はデータのバックアップはもちろん、十分に注意して実施してください。
 
# ライセンス表記
<a rel="license" href="http://creativecommons.org/licenses/by-sa/4.0/"><img alt="クリエイティブ・コモンズ・ライセンス" style="border-width:0" src="https://i.creativecommons.org/l/by-sa/4.0/88x31.png" /></a><br />この記事は<a rel="license" href="http://creativecommons.org/licenses/by-sa/4.0/">クリエイティブ・コモンズ 表示 - 継承 4.0 国際 ライセンス</a>の下に提供されています。