はじめに
これまで、Windows 10(以降Windowsと記載します)の標準NVMeデバイスドライバを使用して、ユーザ空間で動作するプログラムからNVMe SSDに対していくつかのコマンドを発行する方法を紹介しました(その1、その2、その3)。
この記事では、NVMeが規定する機能の設定内容を取得するGet Features
というコマンドを、Windowsの標準NVMeデバイスドライバを使用して発行する方法を説明します。
Get Features
コマンドと対を成す、「機能の設定」を行うSet Features
コマンドも説明できればよかったのですが、Windowsの標準NVMeデバイスドライバを使う限りSet Features
コマンドを使うことで設定内容を変更可能な機能がほとんどないようですので、ここではGet Features
コマンドのみ説明します。
なお、この記事でまとめた内容の動作確認環境は、以前の記事の動作確認環境とほぼ同一ですので、そちらを参照してください。また、完全なコードはGithubに置いてありますのでそちらをご覧ください。
まとめ
-
Get Features
コマンドを使用すると、NVMe SSDの機能の「現在の設定」を取得できる - ただし、Windowsの標準NVMeデバイスドライバを使う場合、取得が可能な機能は限られる
NVMe仕様における"Feature"とは
NVMe仕様[1]におけるGet Features
コマンドは、NVMeデバイス(面倒なので、以降「デバイス」=「SSD」として"SSD"と記載します)の各種機能に関する、現時点での設定内容を取得するためのコマンドです。
NVMe仕様は、Get Features
コマンドの対象となる機能(Feature)として、以下のものを定義しています。
表1:NVMe仕様で定義されている機能(NVMe仕様書から抜粋)
表1内の機能のうち、例えば、Feature Identifier (FID) = 06hのVolatile Write Cache
は揮発性ライトキャッシュ、FID = 02hのPower Management
は省電力機能、そしてFID = 04hのTemperature Threshold
は温度閾値に関する設定となります。
"Feature"の仕組み
Get Features
コマンドの説明に入る前に、NVMe仕様が定義する「機能」の仕組みについて説明します。
NVMe仕様における「機能(Feature)」は、下記のような特徴を持ち、それらの設定内容をGet Features
コマンドで取得できます。
- 機能の設定内容(値)には、「現在の値(Current)」と「デフォルト値(Default)」と「保存された値(Saved)」の3種類がある
- 各機能に下記の「属性(Supported Capabilities)」が存在する
- 設定を変更できるかどうか(Changeable)
- 設定を保存できるかどうか(Saveable)
- Namespace毎に適用される設定なのかコントローラ全体に適用される設定なのか
これらの仕様は、仕様書5.13節"Get Features command"および5.21節"Set Features command"に記載されています。
Get Features
コマンドでどの内容を取得するかは、以下の部分で設定します(青四角で囲った部分)。
図1:Get Features
コマンドで取得する値の設定方法(NVMe仕様書より抜粋)
Get Features
コマンドの発行
それでは、具体的にGet Features
コマンドを発行する手順を見ていきます。
以下のフローチャートは、Windowsの標準NVMeデバイスドライバを使用してNVMe SSDにGet Features
コマンドを発行する手順です。
といっても、これまでに説明した他のコマンドの発行手順と大差はありません。
デバイスハンドルを取得する方法は、以前の別記事で説明した通りですので、説明は省略します。
この記事では、機能(Feature)の例としてVolatile Write Cache
を取り上げます。
「コマンド発行用構造体作成」以降の処理について、以下にコード片を示します。行頭の行番号は、説明のために付与したものです。エラー処理に関するコードは省略しています。
このコードは、Microsoftの説明[2]にしたがって書き起こしたものです。
1: int iResult = -1;
2: PVOID buffer = NULL;
3: ULONG bufferLength = 0;
4: ULONG returnedLength = 0;
5: uint32_t resultData = 0;
6: PSTORAGE_PROPERTY_QUERY query = NULL;
7: PSTORAGE_PROTOCOL_SPECIFIC_DATA protocolData = NULL;
8: PSTORAGE_PROTOCOL_DATA_DESCRIPTOR protocolDataDescr = NULL;
9: NVME_CDW10_GET_FEATURES cdw10 = { 0 };
// Allocate buffer for use.
10: bufferLength = offsetof(STORAGE_PROPERTY_QUERY, AdditionalParameters) + sizeof(STORAGE_PROTOCOL_SPECIFIC_DATA);
11: buffer = malloc(bufferLength);
12: ZeroMemory(buffer, bufferLength);
13: query = (PSTORAGE_PROPERTY_QUERY)buffer;
14: protocolDataDescr = (PSTORAGE_PROTOCOL_DATA_DESCRIPTOR)buffer;
15: protocolData = (PSTORAGE_PROTOCOL_SPECIFIC_DATA)query->AdditionalParameters;
16: query->PropertyId = StorageDeviceProtocolSpecificProperty;
17: query->QueryType = PropertyStandardQuery;
18: cdw10.FID = NVME_FEATURE_VOLATILE_WRITE_CACHE;
19: cdw10.SEL = NVME_FEATURE_VALUE_CURRENT;
20: protocolData->ProtocolType = ProtocolTypeNvme;
21: protocolData->DataType = NVMeDataTypeFeature;
22: protocolData->ProtocolDataRequestValue = (DWORD)(cdw10.AsUlong);
23: protocolData->ProtocolDataRequestSubValue = 0;
24: protocolData->ProtocolDataOffset = 0;
25: protocolData->ProtocolDataLength = 0;
// Send request down.
26: iResult = DeviceIoControl(_hDevice,
IOCTL_STORAGE_QUERY_PROPERTY,
buffer,
bufferLength,
buffer,
bufferLength,
&returnedLength,
NULL);
27: resultData = (uint32_t)(protocolDataDescr->ProtocolSpecificData.FixedProtocolReturnData);
6行目に登場する構造体STORAGE_PROPERTY_QUERY
は、ストレージに問い合わせ(クエリ)をするようなコマンドを発行する際に使用するデータ構造です。
これはGet Log Page
コマンドの発行時にも使ったもので、クエリタイプの設定(17行目)が必要です。
9行目で定義している変数の型は、Get Features
コマンド用にnvme.h
で定義されている構造体です。
Get Features
コマンドでは、Feature IDなどをCommand DWord 10 (CDW10)に設定します。このCDW10の設定内容はGet Features
コマンド専用ですので、nvme.h
に構造体が定義されています。
具体的なFeature IDと図1で示した取得内容は、18行目と19行目で設定して、22行目でコマンド発行用構造体に設定しています。
サンプルコードでは、19行目にNVME_FEATURE_VALUE_CURRENT
を指定しています。これは「現在の値(Current)」の取得を要求するための値です。「デフォルト値(Default)」や「保存された値(Saved)」そして「属性」に対しても、同様にnvme.h
に定数が定義されていますので、それを指定します。
今回はVolatile Write Cache
ですので、FIDにはNVME_FEATURE_VOLATILE_WRITE_CACHE
(06h)を設定します。
順序が逆になってしまいましたが、10行目でバッファサイズを計算して11行目でメモリを確保しているのが、コマンド発行用構造体です。
Volatile Write Cache
機能の場合、Get Features
コマンドの結果としてSSDから受け取るデータはDWord(32ビット)のデータひとつだけですので、SSDから受け取るデータのためのメモリ領域を改めて確保する必要はありません。
そして、21行目でGet Features
コマンドであることを設定した上で、26行目でDeviceIoControl()
を呼び出して、コマンド発行を要求します。
DeviceIoControl()
が成功すると、コマンド発行用構造体の中に、SSDから受け取ったデータが格納されますので、27行目のように参照します。
受信したデータは、NVMe仕様書5.21.1節"Feature Specific Information"に記載されている各機能に関するデータ構造にしたがって解析します。
以下に、とあるNVMe SSDから取得したVolatile Write Cache
の設定内容を示します。なお、フィールド名や説明文の付与や出力の整形はプログラム中で行ったものです。
解釈はNVMe仕様書の5.21.1.6節に従って行います。
[I] Volatile Write Cache:
bit [ 0] Volatile Write Cache Enable (WCE), (0) disabled, (1) enabled
Current = 1
Default = 1
Saved = 1
Capabilities: this feature is
bit [ 2] 1 = (1) changeable, (0) not changeable
bit [ 1] 0 = (1) namespace specific, (0) for entire controller
bit [ 0] 0 = (1) saveable, (0) not saveable
この結果から、このNVMe SSDのVolatile Write Cache
機能は以下のようになっていることがわかります。
- 現在の設定値は「有効(
Current = 1
)」 - デフォルトの設定値は「有効(
Default = 1
)」 - 保存されている値は「有効(
Saved = 1
)」 - 設定値の変更は「可能(
changeable
)」 - この設定値は「コントローラ全体に適用される設定(
for entire controller
)」 - この設定値は「保存できない(
not saveable
)」
わかりにくいですが、実際には、Current
とDefault
とSaved
の設定値を取得するためにそれぞれGet Features
コマンドを発行する必要があります。
つまり、上記の結果を取得するために、属性(Capability)を取得するためのコマンド発行分と合わせて合計4回Get Features
コマンドを発行しています。
まとめ
この記事では、NVMe仕様が定める「機能(Feature)」について説明し、Windowsの標準NVMeデバイスドライバを使って、Get Features
コマンドでその機能の設定内容を取得する方法をまとめました。
NVMe仕様が定める機能(Feature)は仕様が新しくなるにつれてどんどん数が増えているのですが、Windowsの標準NVMeデバイスドライバを使う方法では、Set Features
コマンドによる設定内容の変更はもちろん、Get Features
コマンドによる設定内容の取得ができない機能も多いです。
とはいえ、この記事で紹介した方法は、Windowsの標準NVMeデバイスドライバを使用していますので、とても手軽に試せることが特徴です。
なお、Windowsの標準NVMeデバイスドライバを使用した場合の、各機能についての設定取得可否については、Githubにまとめてあります。
参考文献
[1] NVM Express, "NVM ExpressTM Base Specification", Revision 1.3d, March 20, 2019
[2] Microsoft, "Working with NVMe drives", 最終閲覧日2020年9月16日
ライセンス表記
この記事はクリエイティブ・コモンズ 表示 - 継承 4.0 国際ライセンスの下に提供されています。