Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Gilt für:
- Windows 10
- Windows Server 2016
Erfahren Sie, wie Sie mit NVMe-Geräten mit hoher Geschwindigkeit aus Ihrer Windows-Anwendung arbeiten. Der Gerätezugriff wird über StorNVMe.sys aktiviert, den in Windows Server 2012 R2 und Windows 8.1 eingeführten Inbox-Treiber. Es ist auch für Windows 7-Geräte über einen KB-Hot Fix verfügbar. In Windows 10 wurden mehrere neue Features eingeführt, darunter ein Pass-Through-Mechanismus für anbieterspezifische NVMe-Befehle und Updates für vorhandene IOCTLs.
Dieses Thema enthält eine Übersicht über allgemeine APIs, die Sie für den Zugriff auf NVMe-Laufwerke in Windows 10 verwenden können. Es beschreibt auch Folgendes:
- So senden Sie einen anbieterspezifischen NVMe-Befehl mit Pass-Through
- Wie man einen Befehl zum "Identifizieren", "Features abrufen" oder "Protokollseiten abrufen" an das NVMe-Laufwerk sendet
- Wie man Temperaturinformationen von einem NVMe-Laufwerk abruft
- So wird's ausgeführt: Verhalten ändernde Befehle, z. B. Festlegen von Temperaturschwellenwerten
APIs für das Arbeiten mit NVMe-Laufwerken
Sie können die folgenden allgemeinen APIs verwenden, um auf NVMe-Laufwerke in Windows 10 zuzugreifen. Diese APIs finden Sie in winioctl.h für Benutzermodusanwendungen und ntddstor.h für Kernelmodustreiber. Weitere Informationen zu Headerdateien finden Sie unter Headerdateien.
IOCTL_STORAGE_PROTOCOL_COMMAND : Verwenden Sie diese IOCTL mit der STORAGE_PROTOCOL_COMMAND Struktur, um NVMe-Befehle ausstellen zu können. Diese IOCTL aktiviert NVMe Pass-Through und unterstützt das Command Effects-Protokoll in NVMe. Sie können es mit anbieterspezifischen Befehlen verwenden. Weitere Informationen finden Sie im Pass-Through-Mechanismus.
STORAGE_PROTOCOL_COMMAND : Diese Eingabepufferstruktur enthält ein ReturnStatus-Feld , das verwendet werden kann, um die folgenden Statuswerte zu melden.
- SPEICHERPROTOKOLL_STATUS_AUSSTEHEND
- STORAGE_PROTOCOL_STATUS_SUCCESS
- STORAGE_PROTOCOL_STATUS_ERROR
- STORAGE_PROTOCOL_STATUS_INVALID_REQUEST
- SPEICHER_PROTOCOLSTATUS_KEIN_GERÄT
- STORAGE_PROTOCOL_STATUS_BUSY
- STORAGE_PROTOCOL_STATUS_DATA_OVERRUN
- STORAGE_PROTOCOL_STATUS_INSUFFICIENT_RESOURCES
- STORAGE_PROTOCOL_STATUS_NOT_SUPPORTED
IOCTL_STORAGE_QUERY_PROPERTY : Verwenden Sie diese IOCTL mit der STORAGE_PROPERTY_QUERY Struktur, um Geräteinformationen abzurufen. Weitere Informationen finden Sie unter protokollspezifische Abfragen und Temperaturabfragen.
STORAGE_PROPERTY_QUERY : Diese Struktur enthält die Felder PropertyId und AdditionalParameters , um die daten anzugeben, die abgefragt werden sollen. Verwenden Sie in der abgelegten PropertyId die STORAGE_PROPERTY_ID-Aufzählung , um den Datentyp anzugeben. Verwenden Sie das Feld "AdditionalParameters ", um je nach Datentyp weitere Details anzugeben. Verwenden Sie für protokollspezifische Daten die STORAGE_PROTOCOL_SPECIFIC_DATA Struktur im Feld "AdditionalParameters ". Verwenden Sie für Temperaturdaten die STORAGE_TEMPERATURE_INFO Struktur im Feld "AdditionalParameters ".
STORAGE_PROPERTY_ID : Diese Enumeration enthält neue Werte, mit denen IOCTL_STORAGE_QUERY_PROPERTY protokollspezifische und Temperaturinformationen abrufen können.
StorageAdapterProtocolSpecificProperty: Wenn
ProtocolType=ProtocolTypeNvmeundDataType=NVMeDataTypeLogPageerfüllt sind, sollten Aufrufer 512 Byte große Datenblöcke anfordern.StorageDeviceProtocolSpecificProperty
Verwenden Sie eine dieser protokollspezifischen Eigenschaften-IDs in Kombination mit STORAGE_PROTOCOL_SPECIFIC_DATA , um protokollspezifische Daten in der STORAGE_PROTOCOL_DATA_DESCRIPTOR-Struktur abzurufen.
- StorageAdapterTemperatureProperty
- SpeichergerättemperaturEigenschaft
Verwenden Sie eine dieser Temperatureigenschaften-IDs, um Temperaturdaten in der STORAGE_TEMPERATURE_DATA_DESCRIPTOR Struktur abzurufen.
STORAGE_PROTOCOL_SPECIFIC_DATA : Abrufen von NVMe-spezifischen Daten, wenn diese Struktur für das Feld "AdditionalParameters " von STORAGE_PROPERTY_QUERY verwendet wird und ein STORAGE_PROTOCOL_NVME_DATA_TYPE Enumerationswert angegeben wird. Verwenden Sie einen der folgenden STORAGE_PROTOCOL_NVME_DATA_TYPE Werte im Feld "Datentyp " der STORAGE_PROTOCOL_SPECIFIC_DATA Struktur:
- Verwenden Sie NVMeDataTypeIdentify, um Daten des Verantwortlichen abzurufen oder Namespacedaten zu identifizieren.
- Verwenden Sie NVMeDataTypeLogPage-, um Protokollseiten (einschließlich SMART/Health-Daten) abzurufen.
- Verwenden Sie NVMeDataTypeFeature-, um Features des NVMe-Laufwerks abzurufen.
STORAGE_TEMPERATURE_INFO : Diese Struktur dient zum Halten bestimmter Temperaturdaten. Sie wird in der STORAGE_TEMERATURE_DATA_DESCRIPTOR verwendet, um die Ergebnisse einer Temperaturabfrage zurückzugeben.
IOCTL_STORAGE_SET_TEMPERATURE_THRESHOLD : Verwenden Sie diese IOCTL mit der STORAGE_TEMPERATURE_THRESHOLD Struktur, um Temperaturschwellenwerte festzulegen. Weitere Informationen finden Sie unter Verhaltensänderungsbefehle.
STORAGE_TEMPERATURE_THRESHOLD : Diese Struktur wird als Eingabepuffer verwendet, um den Temperaturschwellenwert anzugeben. Das Feld "OverThreshold " (boolescher Wert) gibt an, ob das Feld "Schwellenwert " der wert über dem Schwellenwert ist oder nicht (andernfalls ist es der untere Schwellenwert).
Pass-Through-Mechanismus
Befehle, die nicht in der NVMe-Spezifikation definiert sind, sind für das Hostbetriebssystem am schwierigsten zu behandeln – der Host hat keinen Einblick in die Auswirkungen, die die Befehle auf dem Zielgerät haben können, die verfügbar gemachte Infrastruktur (Namespaces/Blockgrößen) und sein Verhalten.
Um gerätespezifische Befehle besser über den Windows-Speicherstapel zu übertragen, ermöglicht ein neuer Pass-Through-Mechanismus die Weiterleitung von anbieterspezifischen Befehlen. Dieses Pass-Through-Rohr wird auch bei der Entwicklung von Management- und Testwerkzeugen helfen. Dieser Pass-Through-Mechanismus erfordert jedoch die Verwendung des Befehlseffekteprotokolls. Darüber hinaus erfordert StoreNVMe.sys, dass alle Befehle, nicht nur Pass-Through-Befehle, im Befehlseffektprotokoll beschrieben werden.
Von Bedeutung
StorNVMe.sys und Storport.sys blockieren jeden Befehl an ein Gerät, wenn dieser nicht im Protokoll der Befehlsauswirkungen beschrieben ist.
Unterstützung des Befehlseffektprotokolls
Das Befehlseffekteprotokoll (wie in Befehlen unterstützt und Effekte beschrieben, Abschnitt 5.10.1.5 der NVMe-Spezifikation 1.2) ermöglicht die Beschreibung der Auswirkungen anbieterspezifischer Befehle zusammen mit spezifikationsdefinierte Befehle. Dies erleichtert sowohl die Überprüfung der Befehlsunterstützung als auch die Optimierung des Befehlsverhaltens und sollte daher für den gesamten Satz von Befehlen implementiert werden, die das Gerät unterstützt. Die folgenden Bedingungen beschreiben das Ergebnis, wie der Befehl basierend auf dem Eintrag "Command Effects Log" gesendet wird.
Für einen bestimmten Befehl, der im Befehlseffektprotokoll beschrieben wird...
While:
Befehl Unterstützt (CSUPP) wird auf "1" festgelegt, um zu signalisieren, dass der Befehl vom Controller (Bit 01) unterstützt wird.
Hinweis
Wenn CSUPP auf "0" festgelegt ist (was bedeutet, dass der Befehl nicht unterstützt wird) wird der Befehl blockiert.
Und wenn eine der folgenden Optionen festgelegt ist:
- Die Änderung der Controllerfähigkeiten (Controller Capability Change, CCC) ist auf "1" gesetzt, was bedeutet, dass der Befehl die Controllerfähigkeiten ändern kann (Bit 04).
- Die Änderung des Namespace-Inventars (Namespace Inventory Change, NIC) ist auf "1" festgelegt, was anzeigt, dass der Befehl die Anzahl oder die Fähigkeiten für mehrere Namespaces (Bit 03) ändern kann.
- Namespace Capability Change (NCC) ist auf "1" festgelegt, was bedeutet, dass der Befehl die Funktionen eines einzelnen Namespaces (Bit 02) ändern kann.
- Die Befehlsübermittlung und -ausführung (COMMAND Submission and Execution, CSE) ist auf 001b oder 010b festgelegt, was bedeutet, dass der Befehl möglicherweise übermittelt wird, wenn kein anderer ausstehender Befehl an denselben oder einen Namespace gesendet wird, und dass ein anderer Befehl nicht an denselben oder einen Namespace gesendet werden sollte, bis dieser Befehl abgeschlossen ist (Bits 18:16)
Dann wird der Befehl als einziger ausstehender Befehl an den Adapter gesendet.
else if:
- Die Befehlsübermittlung und -ausführung (CSE) ist auf 001b festgelegt, was bedeutet, dass der Befehl gesendet werden kann, wenn kein anderer ausstehender Befehl an denselben Namespace vorhanden ist und dass ein anderer Befehl erst dann an denselben Namespace gesendet werden sollte, wenn dieser Befehl abgeschlossen ist (Bits 18:16)
Dann wird der Befehl als einziger noch ausstehender Befehl an das Logical Unit Number-Objekt (LUN) gesendet.
Andernfalls wird der Befehl zusammen mit anderen Befehlen gesendet, die ohne Verzögerung ausgeführt werden. Wenn beispielsweise ein anbieterspezifischer Befehl an das Gerät gesendet wird, um statistische Informationen abzurufen, die nicht spezifikationsdefiniert sind, sollte kein Risiko bestehen, das Verhalten oder die Funktion des Geräts zum Ausführen von E/A-Befehlen zu ändern. Solche Anforderungen könnten parallel zu E/A gewartet werden, und es wäre kein Anhalten erforderlich.
Verwenden von IOCTL_STORAGE_PROTOCOL_COMMAND zum Senden von Befehlen
Pass-through kann mit dem in Windows 10 eingeführten IOCTL_STORAGE_PROTOCOL_COMMAND durchgeführt werden. Diese IOCTL wurde so konzipiert, dass sie ein ähnliches Verhalten wie die vorhandenen SCSI- und ATA-Pass-Through-IOCTLs aufweist, um einen eingebetteten Befehl an das Zielgerät zu senden. Über diese IOCTL kann Pass-Through an ein Speichergerät gesendet werden, einschließlich eines NVMe-Laufwerks.
In NVMe lässt die IOCTL beispielsweise das Senden der folgenden Befehlscodes zu.
- Anbieterspezifische Administratorbefehle (C0h – FFh)
- Anbieterspezifische NVMe-Befehle (80h – FFh)
Wie bei allen anderen IOCTLs verwenden Sie DeviceIoControl , um die Pass-Through-IOCTL nach unten zu senden. Die IOCTL wird mit der STORAGE_PROTOCOL_COMMAND Eingabepufferstruktur aufgefüllt, die in ntddstor.h gefunden wurde. Füllen Sie das Feld "Befehl" mit dem anbieterspezifischen Befehl auf.
typedef struct _STORAGE_PROTOCOL_COMMAND {
ULONG Version; // STORAGE_PROTOCOL_STRUCTURE_VERSION
ULONG Length; // sizeof(STORAGE_PROTOCOL_COMMAND)
STORAGE_PROTOCOL_TYPE ProtocolType;
ULONG Flags; // Flags for the request
ULONG ReturnStatus; // return value
ULONG ErrorCode; // return value, optional
ULONG CommandLength; // non-zero value should be set by caller
ULONG ErrorInfoLength; // optional, can be zero
ULONG DataToDeviceTransferLength; // optional, can be zero. Used by WRITE type of request.
ULONG DataFromDeviceTransferLength; // optional, can be zero. Used by READ type of request.
ULONG TimeOutValue; // in unit of seconds
ULONG ErrorInfoOffset; // offsets need to be pointer aligned
ULONG DataToDeviceBufferOffset; // offsets need to be pointer aligned
ULONG DataFromDeviceBufferOffset; // offsets need to be pointer aligned
ULONG CommandSpecific; // optional information passed along with Command.
ULONG Reserved0;
ULONG FixedProtocolReturnData; // return data, optional. Some protocol, such as NVMe, may return a small amount data (DWORD0 from completion queue entry) without the need of separate device data transfer.
ULONG Reserved1[3];
_Field_size_bytes_full_(CommandLength) UCHAR Command[ANYSIZE_ARRAY];
} STORAGE_PROTOCOL_COMMAND, *PSTORAGE_PROTOCOL_COMMAND;
Der anbieterspezifische Befehl, der gesendet werden soll, sollte im oben hervorgehobenen Feld ausgefüllt werden. Beachten Sie bitte noch einmal, dass das Protokoll der Befehlswirkungen für Pass-Through-Kommandos implementiert werden muss. Insbesondere müssen diese Befehle im Befehlseffektprotokoll als unterstützt gemeldet werden (weitere Informationen finden Sie im vorherigen Abschnitt). Beachten Sie außerdem, dass PRP-Felder treiberspezifisch sind, damit Anwendungen, die Befehle senden, diese als 0belassen können.
Schließlich ist diese Pass-Through-IOCTL für das Senden von herstellerspezifischen Befehlen vorgesehen. Um andere Administrator- oder nicht anbieterspezifische NVMe-Befehle wie "Identifizieren" zu senden, sollte diese Pass-Through-IOCTL nicht verwendet werden. Beispielsweise sollten IOCTL_STORAGE_QUERY_PROPERTY zum Identifizieren oder Abrufen von Protokollseiten verwendet werden. Weitere Informationen finden Sie im nächsten Abschnitt, protokollspezifische Abfragen.
Aktualisieren Sie die Firmware nicht über den Pass-Through-Mechanismus
Firmwaredownload- und Aktivierungsbefehle sollten nicht mithilfe von Pass-Through gesendet werden. IOCTL_STORAGE_PROTOCOL_COMMAND sollten nur für anbieterspezifische Befehle verwendet werden.
Verwenden Sie stattdessen die folgenden allgemeinen Speicher-IOCTLs (eingeführt in Windows 10), um zu verhindern, dass Anwendungen direkt die SCSI-Miniport-Version der Firmware-IOCTL verwenden. Speichertreiber übersetzen die IOCTL entweder in einen SCSI-Befehl oder die SCSI-Miniport-Version des IOCTL zum Miniport.
Diese IOCTLs werden für die Entwicklung von Firmwareupgradetools in Windows 10 und Windows Server 2016 empfohlen:
Zum Abrufen von Speicherinformationen und zum Aktualisieren der Firmware unterstützt Windows auch PowerShell-Cmdlets , um dies schnell zu erledigen:
Get-StorageFirmwareInfoUpdate-StorageFirmware
Zurückgeben von Fehlern über den Pass-Through-Mechanismus
Ähnlich wie bei SCSI- und ATA-Pass-Through-IOCTLs meldet der IOCTL zurück, ob ein Befehl oder eine Anforderung erfolgreich an den Miniport oder das Gerät gesendet wurde. In der STORAGE_PROTOCOL_COMMAND-Struktur gibt die IOCTL den Status über das Feld "ReturnStatus " zurück.
Beispiel: Senden eines anbieterspezifischen Befehls
In diesem Beispiel wird ein beliebiger anbieterspezifischer Befehl (0xFF) über Pass-Through an ein NVMe-Laufwerk gesendet. Der folgende Code weist einen Puffer zu, initialisiert eine Abfrage und sendet dann den Befehl über DeviceIoControl an das Gerät.
ZeroMemory(buffer, bufferLength);
protocolCommand = (PSTORAGE_PROTOCOL_COMMAND)buffer;
protocolCommand->Version = STORAGE_PROTOCOL_STRUCTURE_VERSION;
protocolCommand->Length = sizeof(STORAGE_PROTOCOL_COMMAND);
protocolCommand->ProtocolType = ProtocolTypeNvme;
protocolCommand->Flags = STORAGE_PROTOCOL_COMMAND_FLAG_ADAPTER_REQUEST;
protocolCommand->CommandLength = STORAGE_PROTOCOL_COMMAND_LENGTH_NVME;
protocolCommand->ErrorInfoLength = sizeof(NVME_ERROR_INFO_LOG);
protocolCommand->DataFromDeviceTransferLength = 4096;
protocolCommand->TimeOutValue = 10;
protocolCommand->ErrorInfoOffset = FIELD_OFFSET(STORAGE_PROTOCOL_COMMAND, Command) + STORAGE_PROTOCOL_COMMAND_LENGTH_NVME;
protocolCommand->DataFromDeviceBufferOffset = protocolCommand->ErrorInfoOffset + protocolCommand->ErrorInfoLength;
protocolCommand->CommandSpecific = STORAGE_PROTOCOL_SPECIFIC_NVME_ADMIN_COMMAND;
command = (PNVME_COMMAND)protocolCommand->Command;
command->CDW0.OPC = 0xFF;
command->u.GENERAL.CDW10 = 0xto_fill_in;
command->u.GENERAL.CDW12 = 0xto_fill_in;
command->u.GENERAL.CDW13 = 0xto_fill_in;
//
// Send request down.
//
result = DeviceIoControl(DeviceList[DeviceIndex].Handle,
IOCTL_STORAGE_PROTOCOL_COMMAND,
buffer,
bufferLength,
buffer,
bufferLength,
&returnedLength,
NULL
);
In diesem Beispiel erwarten wir protocolCommand->ReturnStatus == STORAGE_PROTOCOL_STATUS_SUCCESS, dass der Befehl auf dem Gerät erfolgreich ausgeführt wurde.
Protokollspezifische Abfragen
Windows 8.1 hat IOCTL_STORAGE_QUERY_PROPERTY für den Datenabruf eingeführt. In Windows 10 wurde die IOCTL erweitert, um häufig angeforderte NVMe-Features wie "Protokollseiten abrufen", "Features abrufen" und " Identifizieren" zu unterstützen. Dies ermöglicht das Abrufen von NVMe-spezifischen Informationen für Überwachungs- und Bestandszwecke.
Der Eingabepuffer für die IOCTL, STORAGE_PROPERTY_QUERY (von Windows 10 und höher) wird hier gezeigt:
typedef struct _STORAGE_PROPERTY_QUERY {
STORAGE_PROPERTY_ID PropertyId;
STORAGE_QUERY_TYPE QueryType;
UCHAR AdditionalParameters[1];
} STORAGE_PROPERTY_QUERY, *PSTORAGE_PROPERTY_QUERY;
Wenn Sie IOCTL_STORAGE_QUERY_PROPERTY zum Abrufen von NVMe-protokollspezifischen Informationen im STORAGE_PROTOCOL_DATA_DESCRIPTOR verwenden, konfigurieren Sie die STORAGE_PROPERTY_QUERY-Struktur wie folgt:
- Weisen Sie einen Puffer zu, der sowohl eine STORAGE_PROPERTY_QUERY als auch eine STORAGE_PROTOCOL_SPECIFIC_DATA Struktur enthält.
- Legen Sie das PropertyID- Feld auf StorageAdapterProtocolSpecificProperty oder StorageDeviceProtocolSpecificProperty für eine Controller- oder Geräte-/Namespaceanforderung fest.
- Legen Sie das feld QueryType auf PropertyStandardQuery-fest.
- Füllen Sie die STORAGE_PROTOCOL_SPECIFIC_DATA Struktur mit den gewünschten Werten aus. Der Anfang des STORAGE_PROTOCOL_SPECIFIC_DATA ist das AdditionalParameters Feld von STORAGE_PROPERTY_QUERY.
Die STORAGE_PROTOCOL_SPECIFIC_DATA-Struktur (von Windows 10 und höher) wird hier gezeigt:
typedef struct _STORAGE_PROTOCOL_SPECIFIC_DATA {
STORAGE_PROTOCOL_TYPE ProtocolType;
ULONG DataType;
ULONG ProtocolDataRequestValue;
ULONG ProtocolDataRequestSubValue;
ULONG ProtocolDataOffset;
ULONG ProtocolDataLength;
ULONG FixedProtocolReturnData;
ULONG Reserved[3];
} STORAGE_PROTOCOL_SPECIFIC_DATA, *PSTORAGE_PROTOCOL_SPECIFIC_DATA;
Um einen Typ von NVMe-protokollspezifischen Informationen anzugeben, konfigurieren Sie die STORAGE_PROTOCOL_SPECIFIC_DATA Struktur wie folgt:
- Legen Sie das feld ProtocolType auf ProtocolTypeNVMe-fest.
- Legen Sie das DataType Feld auf einen Enumerationswert fest, der durch STORAGE_PROTOCOL_NVME_DATA_TYPEdefiniert wird:
- Verwenden Sie NVMeDataTypeIdentify, um Daten des Verantwortlichen abzurufen oder Namespacedaten zu identifizieren.
- Verwenden Sie NVMeDataTypeLogPage-, um Protokollseiten (einschließlich SMART/Health-Daten) abzurufen.
- Verwenden Sie NVMeDataTypeFeature-, um Features des NVMe-Laufwerks abzurufen.
Wenn ProtocolTypeNVMe als ProtocolType verwendet wird, können Abfragen für protokollspezifische Informationen parallel mit anderen E/A auf dem NVMe-Laufwerk abgerufen werden.
Von Bedeutung
Für eine IOCTL_STORAGE_QUERY_PROPERTY, die eine STORAGE_PROPERTY_ID vom Typ StorageAdapterProtocolSpecificProperty verwendet und deren STORAGE_PROTOCOL_SPECIFIC_DATA- oder STORAGE_PROTOCOL_SPECIFIC_DATA_EXT-Struktur auf ProtocolType=ProtocolTypeNvme und DataType=NVMeDataTypeLogPage setzt, legen Sie das Mitglied ProtocolDataLength derselben Struktur auf einen Mindestwert von 512 (Bytes) fest.
Die folgenden Beispiele veranschaulichen NVMe-protokollspezifische Abfragen.
Beispiel: NVMe-Abfrage identifizieren
In diesem Beispiel wird die Identify-Anforderung an ein NVMe-Laufwerk gesendet. Der folgende Code initialisiert die Abfragedatenstruktur und sendet dann den Befehl über DeviceIoControl an das Gerät.
BOOL result;
PVOID buffer = NULL;
ULONG bufferLength = 0;
ULONG returnedLength = 0;
PSTORAGE_PROPERTY_QUERY query = NULL;
PSTORAGE_PROTOCOL_SPECIFIC_DATA protocolData = NULL;
PSTORAGE_PROTOCOL_DATA_DESCRIPTOR protocolDataDescr = NULL;
//
// Allocate buffer for use.
//
bufferLength = FIELD_OFFSET(STORAGE_PROPERTY_QUERY, AdditionalParameters) + sizeof(STORAGE_PROTOCOL_SPECIFIC_DATA) + NVME_MAX_LOG_SIZE;
buffer = malloc(bufferLength);
if (buffer == NULL) {
_tprintf(_T("DeviceNVMeQueryProtocolDataTest: allocate buffer failed, exit.\n"));
goto exit;
}
//
// Initialize query data structure to get Identify Controller Data.
//
ZeroMemory(buffer, bufferLength);
query = (PSTORAGE_PROPERTY_QUERY)buffer;
protocolDataDescr = (PSTORAGE_PROTOCOL_DATA_DESCRIPTOR)buffer;
protocolData = (PSTORAGE_PROTOCOL_SPECIFIC_DATA)query->AdditionalParameters;
query->PropertyId = StorageAdapterProtocolSpecificProperty;
query->QueryType = PropertyStandardQuery;
protocolData->ProtocolType = ProtocolTypeNvme;
protocolData->DataType = NVMeDataTypeIdentify;
protocolData->ProtocolDataRequestValue = NVME_IDENTIFY_CNS_CONTROLLER;
protocolData->ProtocolDataRequestSubValue = 0;
protocolData->ProtocolDataOffset = sizeof(STORAGE_PROTOCOL_SPECIFIC_DATA);
protocolData->ProtocolDataLength = NVME_MAX_LOG_SIZE;
//
// Send request down.
//
result = DeviceIoControl(DeviceList[Index].Handle,
IOCTL_STORAGE_QUERY_PROPERTY,
buffer,
bufferLength,
buffer,
bufferLength,
&returnedLength,
NULL
);
ZeroMemory(buffer, bufferLength);
query = (PSTORAGE_PROPERTY_QUERY)buffer;
protocolDataDescr = (PSTORAGE_PROTOCOL_DATA_DESCRIPTOR)buffer;
protocolData = (PSTORAGE_PROTOCOL_SPECIFIC_DATA)query->AdditionalParameters;
query->PropertyId = StorageDeviceProtocolSpecificProperty;
query->QueryType = PropertyStandardQuery;
protocolData->ProtocolType = ProtocolTypeNvme;
protocolData->DataType = NVMeDataTypeLogPage;
protocolData->ProtocolDataRequestValue = NVME_LOG_PAGE_HEALTH_INFO;
protocolData->ProtocolDataRequestSubValue = 0;
protocolData->ProtocolDataOffset = sizeof(STORAGE_PROTOCOL_SPECIFIC_DATA);
protocolData->ProtocolDataLength = sizeof(NVME_HEALTH_INFO_LOG);
//
// Send request down.
//
result = DeviceIoControl(DeviceList[Index].Handle,
IOCTL_STORAGE_QUERY_PROPERTY,
buffer,
bufferLength,
buffer,
bufferLength,
&returnedLength,
NULL
);
//
// Validate the returned data.
//
if ((protocolDataDescr->Version != sizeof(STORAGE_PROTOCOL_DATA_DESCRIPTOR)) ||
(protocolDataDescr->Size != sizeof(STORAGE_PROTOCOL_DATA_DESCRIPTOR))) {
_tprintf(_T("DeviceNVMeQueryProtocolDataTest: Get Identify Controller Data - data descriptor header not valid.\n"));
return;
}
protocolData = &protocolDataDescr->ProtocolSpecificData;
if ((protocolData->ProtocolDataOffset < sizeof(STORAGE_PROTOCOL_SPECIFIC_DATA)) ||
(protocolData->ProtocolDataLength < NVME_MAX_LOG_SIZE)) {
_tprintf(_T("DeviceNVMeQueryProtocolDataTest: Get Identify Controller Data - ProtocolData Offset/Length not valid.\n"));
goto exit;
}
//
// Identify Controller Data
//
{
PNVME_IDENTIFY_CONTROLLER_DATA identifyControllerData = (PNVME_IDENTIFY_CONTROLLER_DATA)((PCHAR)protocolData + protocolData->ProtocolDataOffset);
if ((identifyControllerData->VID == 0) ||
(identifyControllerData->NN == 0)) {
_tprintf(_T("DeviceNVMeQueryProtocolDataTest: Identify Controller Data not valid.\n"));
goto exit;
} else {
_tprintf(_T("DeviceNVMeQueryProtocolDataTest: ***Identify Controller Data succeeded***.\n"));
}
}
Von Bedeutung
Für eine IOCTL_STORAGE_QUERY_PROPERTY, die eine STORAGE_PROPERTY_ID vom Typ StorageAdapterProtocolSpecificProperty verwendet und deren STORAGE_PROTOCOL_SPECIFIC_DATA- oder STORAGE_PROTOCOL_SPECIFIC_DATA_EXT-Struktur auf ProtocolType=ProtocolTypeNvme und DataType=NVMeDataTypeLogPage setzt, legen Sie das Mitglied ProtocolDataLength derselben Struktur auf einen Mindestwert von 512 (Bytes) fest.
Beachten Sie, dass der Aufrufer einen einzelnen Puffer zuordnen muss, der STORAGE_PROPERTY_QUERY und die Größe von STORAGE_PROTOCOL_SPECIFIC_DATA enthält. In diesem Beispiel wird derselbe Puffer für die Eingabe und Ausgabe aus der Eigenschaftenabfrage verwendet. Aus diesem Grund weist der zugeordnete Puffer eine Größe von "FIELD_OFFSET(STORAGE_PROPERTY_QUERY, AdditionalParameters) + sizeof(STORAGE_PROTOCOL_SPECIFIC_DATA) + NVME_MAX_LOG_SIZE" auf. Obwohl separate Puffer sowohl für Die Eingabe als auch für die Ausgabe zugewiesen werden können, empfehlen wir die Verwendung eines einzelnen Puffers zum Abfragen von NVMe-bezogenen Informationen.
identifyControllerData->NN ist die Anzahl der Namespaces (NN). Windows erkennt einen Namespace als physisches Laufwerk.
Beispiel: NvMe Get Log Pages-Abfrage
In diesem Beispiel, basierend auf dem vorherigen, wird die Anforderung " Protokollseiten abrufen" an ein NVMe-Laufwerk gesendet. Der folgende Code bereitet die Abfragedatenstruktur vor und sendet dann den Befehl über DeviceIoControl an das Gerät.
ZeroMemory(buffer, bufferLength);
query = (PSTORAGE_PROPERTY_QUERY)buffer;
protocolDataDescr = (PSTORAGE_PROTOCOL_DATA_DESCRIPTOR)buffer;
protocolData = (PSTORAGE_PROTOCOL_SPECIFIC_DATA)query->AdditionalParameters;
query->PropertyId = StorageDeviceProtocolSpecificProperty;
query->QueryType = PropertyStandardQuery;
protocolData->ProtocolType = ProtocolTypeNvme;
protocolData->DataType = NVMeDataTypeLogPage;
protocolData->ProtocolDataRequestValue = NVME_LOG_PAGE_HEALTH_INFO;
protocolData->ProtocolDataRequestSubValue = 0; // This will be passed as the lower 32 bit of log page offset if controller supports extended data for the Get Log Page.
protocolData->ProtocolDataRequestSubValue2 = 0; // This will be passed as the higher 32 bit of log page offset if controller supports extended data for the Get Log Page.
protocolData->ProtocolDataRequestSubValue3 = 0; // This will be passed as Log Specific Identifier in CDW11.
protocolData->ProtocolDataRequestSubValue4 = 0; // This will map to STORAGE_PROTOCOL_DATA_SUBVALUE_GET_LOG_PAGE definition, then user can pass Retain Asynchronous Event, Log Specific Field.
protocolData->ProtocolDataOffset = sizeof(STORAGE_PROTOCOL_SPECIFIC_DATA);
protocolData->ProtocolDataLength = sizeof(NVME_HEALTH_INFO_LOG);
//
// Send request down.
//
result = DeviceIoControl(DeviceList[Index].Handle,
IOCTL_STORAGE_QUERY_PROPERTY,
buffer,
bufferLength,
buffer,
bufferLength,
&returnedLength,
NULL
);
if (!result || (returnedLength == 0)) {
_tprintf(_T("DeviceNVMeQueryProtocolDataTest: SMART/Health Information Log failed. Error Code %d.\n"), GetLastError());
goto exit;
}
//
// Validate the returned data.
//
if ((protocolDataDescr->Version != sizeof(STORAGE_PROTOCOL_DATA_DESCRIPTOR)) ||
(protocolDataDescr->Size != sizeof(STORAGE_PROTOCOL_DATA_DESCRIPTOR))) {
_tprintf(_T("DeviceNVMeQueryProtocolDataTest: SMART/Health Information Log - data descriptor header not valid.\n"));
return;
}
protocolData = &protocolDataDescr->ProtocolSpecificData;
if ((protocolData->ProtocolDataOffset < sizeof(STORAGE_PROTOCOL_SPECIFIC_DATA)) ||
(protocolData->ProtocolDataLength < sizeof(NVME_HEALTH_INFO_LOG))) {
_tprintf(_T("DeviceNVMeQueryProtocolDataTest: SMART/Health Information Log - ProtocolData Offset/Length not valid.\n"));
goto exit;
}
//
// SMART/Health Information Log Data
//
{
PNVME_HEALTH_INFO_LOG smartInfo = (PNVME_HEALTH_INFO_LOG)((PCHAR)protocolData + protocolData->ProtocolDataOffset);
_tprintf(_T("DeviceNVMeQueryProtocolDataTest: SMART/Health Information Log Data - Temperature %d.\n"), ((ULONG)smartInfo->Temperature[1] << 8 | smartInfo->Temperature[0]) - 273);
_tprintf(_T("DeviceNVMeQueryProtocolDataTest: ***SMART/Health Information Log succeeded***.\n"));
}
Aufrufer können eine STORAGE_PROPERTY_ID von StorageAdapterProtocolSpecificProperty verwenden, wobei die STORAGE_PROTOCOL_SPECIFIC_DATA oder STORAGE_PROTOCOL_SPECIFIC_DATA_EXT Struktur auf ProtocolDataRequestValue=VENDOR_SPECIFIC_LOG_PAGE_IDENTIFIER gesetzt ist, um 512-Byte-Blöcke von anbieterspezifischen Daten anzufordern.
Beispiel: NVMe Get Features-Abfrage
In diesem Beispiel, basierend auf dem vorherigen, wird die Anforderung " Features abrufen " an ein NVMe-Laufwerk gesendet. Der folgende Code bereitet die Abfragedatenstruktur vor und sendet dann den Befehl über DeviceIoControl an das Gerät.
//
// Initialize query data structure to Volatile Cache feature.
//
ZeroMemory(buffer, bufferLength);
query = (PSTORAGE_PROPERTY_QUERY)buffer;
protocolDataDescr = (PSTORAGE_PROTOCOL_DATA_DESCRIPTOR)buffer;
protocolData = (PSTORAGE_PROTOCOL_SPECIFIC_DATA)query->AdditionalParameters;
query->PropertyId = StorageDeviceProtocolSpecificProperty;
query->QueryType = PropertyStandardQuery;
protocolData->ProtocolType = ProtocolTypeNvme;
protocolData->DataType = NVMeDataTypeFeature;
protocolData->ProtocolDataRequestValue = NVME_FEATURE_VOLATILE_WRITE_CACHE;
protocolData->ProtocolDataRequestSubValue = 0;
protocolData->ProtocolDataOffset = 0;
protocolData->ProtocolDataLength = 0;
//
// Send request down.
//
result = DeviceIoControl(DeviceList[Index].Handle,
IOCTL_STORAGE_QUERY_PROPERTY,
buffer,
bufferLength,
buffer,
bufferLength,
&returnedLength,
NULL
);
if (!result || (returnedLength == 0)) {
_tprintf(_T("DeviceNVMeQueryProtocolDataTest: Get Feature - Volatile Cache failed. Error Code %d.\n"), GetLastError());
goto exit;
}
//
// Validate the returned data.
//
if ((protocolDataDescr->Version != sizeof(STORAGE_PROTOCOL_DATA_DESCRIPTOR)) ||
(protocolDataDescr->Size != sizeof(STORAGE_PROTOCOL_DATA_DESCRIPTOR))) {
_tprintf(_T("DeviceNVMeQueryProtocolDataTest: Get Feature - Volatile Cache - data descriptor header not valid.\n"));
return;
}
//
// Volatile Cache
//
{
_tprintf(_T("DeviceNVMeQueryProtocolDataTest: Get Feature - Volatile Cache - %x.\n"), protocolDataDescr->ProtocolSpecificData.FixedProtocolReturnData);
_tprintf(_T("DeviceNVMeQueryProtocolDataTest: ***Get Feature - Volatile Cache succeeded***.\n"));
}
Protokollspezifischer Satz
Beachten Sie, dass in Windows 10 19H1 und höher die IOCTL_STORAGE_SET_PROPERTY erweitert wurde, um NVMe-Set-Features zu unterstützen.
Der Eingabepuffer für die IOCTL_STORAGE_SET_PROPERTY wird hier gezeigt:
typedef struct _STORAGE_PROPERTY_SET {
//
// ID of the property being retrieved
//
STORAGE_PROPERTY_ID PropertyId;
//
// Flags indicating the type of set property being performed
//
STORAGE_SET_TYPE SetType;
//
// Space for additional parameters if necessary
//
UCHAR AdditionalParameters[1];
} STORAGE_PROPERTY_SET, *PSTORAGE_PROPERTY_SET;
Wenn Sie IOCTL_STORAGE_SET_PROPERTY zum Festlegen des NVMe-Features verwenden, konfigurieren Sie die STORAGE_PROPERTY_SET Struktur wie folgt:
- Weisen Sie einen Puffer zu, der sowohl eine STORAGE_PROPERTY_SET als auch eine STORAGE_PROTOCOL_SPECIFIC_DATA_EXT Struktur enthält;
- Legen Sie das PropertyID- Feld auf StorageAdapterProtocolSpecificProperty oder StorageDeviceProtocolSpecificProperty für eine Controller- oder Geräte-/Namespaceanforderung fest.
- Füllen Sie die STORAGE_PROTOCOL_SPECIFIC_DATA_EXT Struktur mit den gewünschten Werten aus. Der Anfang des STORAGE_PROTOCOL_SPECIFIC_DATA_EXT ist das Feld „AdditionalParameters“ von STORAGE_PROPERTY_SET.
Die STORAGE_PROTOCOL_SPECIFIC_DATA_EXT Struktur wird hier gezeigt.
typedef struct _STORAGE_PROTOCOL_SPECIFIC_DATA_EXT {
STORAGE_PROTOCOL_TYPE ProtocolType;
ULONG DataType; // The value will be protocol specific, as defined in STORAGE_PROTOCOL_NVME_DATA_TYPE or STORAGE_PROTOCOL_ATA_DATA_TYPE.
ULONG ProtocolDataValue;
ULONG ProtocolDataSubValue; // Data sub request value
ULONG ProtocolDataOffset; // The offset of data buffer is from beginning of this data structure.
ULONG ProtocolDataLength;
ULONG FixedProtocolReturnData;
ULONG ProtocolDataSubValue2; // First additional data sub request value
ULONG ProtocolDataSubValue3; // Second additional data sub request value
ULONG ProtocolDataSubValue4; // Third additional data sub request value
ULONG ProtocolDataSubValue5; // Fourth additional data sub request value
ULONG Reserved[5];
} STORAGE_PROTOCOL_SPECIFIC_DATA_EXT, *PSTORAGE_PROTOCOL_SPECIFIC_DATA_EXT;
Um einen festzulegenden NVMe-Featuretyp anzugeben, konfigurieren Sie die STORAGE_PROTOCOL_SPECIFIC_DATA_EXT Struktur wie folgt:
- Legen Sie das ProtocolType-Feld auf "ProtocolTypeNvme" fest.
- Legen Sie das DataType-Feld auf den Enumerationswert NVMeDataTypeFeature fest, das von STORAGE_PROTOCOL_NVME_DATA_TYPE definiert wird;
Die folgenden Beispiele veranschaulichen den NVMe-Funktionssatz.
Beispiel: NVMe-Feature-Einstellungen
In diesem Beispiel wird die Anforderung "Features festlegen" an ein NVMe-Laufwerk gesendet. Der folgende Code bereitet die Setdatenstruktur vor und sendet dann den Befehl über DeviceIoControl an das Gerät.
PSTORAGE_PROPERTY_SET setProperty = NULL;
PSTORAGE_PROTOCOL_SPECIFIC_DATA_EXT protocolData = NULL;
PSTORAGE_PROTOCOL_DATA_DESCRIPTOR_EXT protocolDataDescr = NULL;
//
// Allocate buffer for use.
//
bufferLength = FIELD_OFFSET(STORAGE_PROPERTY_SET, AdditionalParameters) + sizeof(STORAGE_PROTOCOL_SPECIFIC_DATA_EXT);
bufferLength += NVME_MAX_LOG_SIZE;
buffer = new UCHAR[bufferLength];
//
// Initialize query data structure to get the desired log page.
//
ZeroMemory(buffer, bufferLength);
setProperty = (PSTORAGE_PROPERTY_SET)buffer;
setProperty->PropertyId = StorageAdapterProtocolSpecificProperty;
setProperty->SetType = PropertyStandardSet;
protocolData = (PSTORAGE_PROTOCOL_SPECIFIC_DATA_EXT)setProperty->AdditionalParameters;
protocolData->ProtocolType = ProtocolTypeNvme;
protocolData->DataType = NVMeDataTypeFeature;
protocolData->ProtocolDataValue = NVME_FEATURE_HOST_CONTROLLED_THERMAL_MANAGEMENT;
protocolData->ProtocolDataSubValue = 0; // This will pass to CDW11.
protocolData->ProtocolDataSubValue2 = 0; // This will pass to CDW12.
protocolData->ProtocolDataSubValue3 = 0; // This will pass to CDW13.
protocolData->ProtocolDataSubValue4 = 0; // This will pass to CDW14.
protocolData->ProtocolDataSubValue5 = 0; // This will pass to CDW15.
protocolData->ProtocolDataOffset = 0;
protocolData->ProtocolDataLength = 0;
//
// Send request down.
//
result = DeviceIoControl(m_deviceHandle,
IOCTL_STORAGE_SET_PROPERTY,
buffer,
bufferLength,
buffer,
bufferLength,
&returnedLength,
NULL
);
Temperaturabfragen
In Windows 10 und höher können IOCTL_STORAGE_QUERY_PROPERTY auch zum Abfragen von Temperaturdaten von NVMe-Geräten verwendet werden.
Um Temperaturinformationen von einem NVMe-Laufwerk im STORAGE_TEMPERATURE_DATA_DESCRIPTOR abzurufen, konfigurieren Sie die STORAGE_PROPERTY_QUERY-Struktur wie folgt:
- Weisen Sie einen Puffer zu, der eine STORAGE_PROPERTY_QUERY Struktur enthält.
- Legen Sie das PropertyID-Feld auf "StorageAdapterTemperatureProperty " oder "StorageDeviceTemperatureProperty " für einen Controller oder eine Geräte-/Namespaceanforderung fest.
- Legen Sie das feld QueryType auf PropertyStandardQuery-fest.
Die STORAGE_TEMPERATURE_INFO Struktur (in Windows 10 und höher verfügbar) wird hier gezeigt:
typedef struct _STORAGE_TEMPERATURE_INFO {
USHORT Index; // Starts from 0. Index 0 may indicate a composite value.
SHORT Temperature; // Signed value; in Celsius.
SHORT OverThreshold; // Signed value; in Celsius.
SHORT UnderThreshold; // Signed value; in Celsius.
BOOLEAN OverThresholdChangable; // Can the threshold value being changed by using IOCTL_STORAGE_SET_TEMPERATURE_THRESHOLD.
BOOLEAN UnderThresholdChangable; // Can the threshold value being changed by using IOCTL_STORAGE_SET_TEMPERATURE_THRESHOLD.
BOOLEAN EventGenerated; // Indicates that notification will be generated when temperature cross threshold.
UCHAR Reserved0;
ULONG Reserved1;
} STORAGE_TEMPERATURE_INFO, *PSTORAGE_TEMPERATURE_INFO;
Verhaltensänderungsbefehle
Befehle, die Geräteattribute bearbeiten oder das Geräteverhalten beeinträchtigen, sind für das Betriebssystem schwieriger zu behandeln. Wenn sich Geräteattribute zur Laufzeit ändern, während E/A verarbeitet wird, können Synchronisierungs- oder Datenintegritätsprobleme auftreten, wenn sie nicht ordnungsgemäß behandelt werden.
Der Befehl " NVMe-Set-Features" ist ein gutes Beispiel für einen Verhaltensänderungsbefehl. Es ermöglicht die Änderung des Schiedsmechanismus und die Festlegung von Temperaturschwellenwerten. Um sicherzustellen, dass in-flight Daten nicht gefährdet sind, wenn verhaltensbeeinflussende Set-Befehle gesendet werden, hält Windows alle E/A-Vorgänge zum NVMe-Gerät an, entwässert Warteschlangen und leert Puffer. Sobald der Setbefehl erfolgreich ausgeführt wurde, wird E/A (sofern möglich) fortgesetzt. Wenn E/A nicht fortgesetzt werden kann, kann eine Geräterücksetzung erforderlich sein.
Festlegen von Temperaturschwellenwerten
Windows 10 hat IOCTL_STORAGE_SET_TEMPERATURE_THRESHOLD eingeführt, ein IOCTL zum Abrufen und Festlegen von Temperaturschwellenwerten. Sie können es auch verwenden, um die aktuelle Temperatur des Geräts zu erhalten. Der Eingabe-/Ausgabepuffer für diese IOCTL ist die STORAGE_TEMPERATURE_INFO Struktur aus dem vorherigen Codeabschnitt.
Beispiel: Festlegen der Überschwellentemperatur
In diesem Beispiel wird die Überschwellentemperatur eines NVMe-Laufwerks festgelegt. Der folgende Code bereitet den Befehl vor und sendet ihn dann über DeviceIoControl an das Gerät.
BOOL result;
ULONG returnedLength = 0;
STORAGE_TEMPERATURE_THRESHOLD setThreshold = {0};
setThreshold.Version = sizeof(STORAGE_TEMPERATURE_THRESHOLD);
setThreshold.Size = sizeof(STORAGE_TEMPERATURE_THRESHOLD);
setThreshold.Flags = STORAGE_TEMPERATURE_THRESHOLD_FLAG_ADAPTER_REQUEST;
setThreshold.Index = SensorIndex;
setThreshold.Threshold = Threshold;
setThreshold.OverThreshold = UpdateOverThreshold;
//
// Send request down.
//
result = DeviceIoControl(DeviceList[DeviceIndex].Handle,
IOCTL_STORAGE_SET_TEMPERATURE_THRESHOLD,
&setThreshold,
sizeof(STORAGE_TEMPERATURE_THRESHOLD),
NULL,
0,
&returnedLength,
NULL
);
Festlegen von herstellerspezifischen Features
Ohne das Befehlseffektprotokoll verfügt der Treiber nicht über die Auswirkungen des Befehls. Aus diesem Grund ist das Befehlseffekteprotokoll erforderlich. Es hilft dem Betriebssystem zu ermitteln, ob ein Befehl hohe Auswirkungen hat und ob er parallel zu anderen Befehlen an das Laufwerk gesendet werden kann.
Das Command Effects Log ist noch nicht präzise genug, um herstellerspezifische Set-Features-Befehle einzuschließen. Aus diesem Grund ist es noch nicht möglich, anbieterspezifische Set-Features-Befehle zu senden. Es ist jedoch möglich, den zuvor erläuterten Pass-Through-Mechanismus zum Senden von anbieterspezifischen Befehlen zu verwenden. Weitere Informationen finden Sie im Pass-Through-Mechanismus.
Headerdateien
Die folgenden Dateien sind für die NVMe-Entwicklung relevant. Diese Dateien sind im Microsoft Windows Software Development Kit (SDK) enthalten.
| Header-Datei | BESCHREIBUNG |
|---|---|
| ntddstor.h | Definiert Konstanten und Typen für den Zugriff auf die Speicherklassentreiber aus dem Kernelmodus. |
| nvme.h | Für andere NVMe-bezogene Datenstrukturen. |
| winioctl.h | Für allgemeine Win32 IOCTL-Definitionen, einschließlich Speicher-APIs für Benutzermodusanwendungen. |