Uso delle unità NVMe
Si applica a:
- Windows 10
- Windows Server 2016
Informazioni su come usare dispositivi NVMe ad alta velocità dall'applicazione Windows. L'accesso al dispositivo è abilitato tramite StorNVMe.sys, il driver in-box introdotto per la prima volta in Windows Server 2012 R2 e Windows 8.1. È disponibile anche per i dispositivi Windows 7 tramite una correzione a caldo kb. In Windows 10 sono state introdotte diverse nuove funzionalità, tra cui un meccanismo pass-through per i comandi NVMe specifici del fornitore e gli aggiornamenti a IOCTLs esistenti.
Questo argomento offre una panoramica delle API di uso generico che è possibile usare per accedere alle unità NVMe in Windows 10. Descrive anche:
- Come inviare un comando NVMe specifico del fornitore con pass-through
- Come inviare un comando Identify, Get Features o Get Log Pages all'unità NVMe
- Come ottenere informazioni sulla temperatura da un'unità NVMe
- Come eseguire comandi di modifica del comportamento, ad esempio l'impostazione delle soglie di temperatura
API per l'uso delle unità NVMe
Puoi usare le API di utilizzo generico seguenti per accedere alle unità NVMe in Windows 10. Queste API sono disponibili in winioctl.h per le applicazioni in modalità utente e ntddstor.h per i driver in modalità kernel. Per altre informazioni sui file di intestazione, vedere File di intestazione.
IOCTL_STORAGE_PROTOCOL_COMMAND: usare questo IOCTL con la struttura STORAGE_PROTOCOL_COMMAND per eseguire comandi NVMe. Questo IOCTL abilita il pass-through NVMe e supporta il log degli effetti dei comandi in NVMe. È possibile usarlo con comandi specifici del fornitore. Per altre info, vedi Meccanismo pass-through.
STORAGE_PROTOCOL_COMMAND : questa struttura di buffer di input include un campo ReturnStatus che può essere usato per segnalare i valori di stato seguenti.
- STORAGE_PROTOCOL_STATUS_PENDING
- STORAGE_PROTOCOL_STATUS_SUCCESS
- STORAGE_PROTOCOL_STATUS_ERROR
- STORAGE_PROTOCOL_STATUS_INVALID_REQUEST
- STORAGE_PROTOCOL_STATUS_NO_DEVICE
- STORAGE_PROTOCOL_STATUS_BUSY
- STORAGE_PROTOCOL_STATUS_DATA_OVERRUN
- STORAGE_PROTOCOL_STATUS_INSUFFICIENT_RESOURCES
- STORAGE_PROTOCOL_STATUS_NOT_SUPPORTED
IOCTL_STORAGE_QUERY_PROPERTY : usare questo IOCTL con la struttura STORAGE_PROPERTY_QUERY per recuperare le informazioni sul dispositivo. Per altre informazioni, vedere Query specifiche del protocollo e Query sulla temperatura.
STORAGE_PROPERTY_QUERY : questa struttura include i campi PropertyId e AdditionalParameters per specificare i dati su cui eseguire query. Nell'oggetto PropertyId archiviato utilizzare l'enumerazione STORAGE_PROPERTY_ID per specificare il tipo di dati. Usare il campo AdditionalParameters per specificare altri dettagli, a seconda del tipo di dati. Per i dati specifici del protocollo, usare la struttura STORAGE_PROTOCOL_SPECIFIC_DATA nel campo AdditionalParameters . Per i dati relativi alla temperatura, usare la struttura STORAGE_TEMPERATURE_INFO nel campo AdditionalParameters .
STORAGE_PROPERTY_ID : questa enumerazione include nuovi valori che consentono IOCTL_STORAGE_QUERY_PROPERTY di recuperare informazioni specifiche del protocollo e sulla temperatura.
- Archiviazione AdapterProtocolSpecificProperty: Se ProtocolType = ProtocolTypeNvme e DataType = NVMeDataTypeLogPage, i chiamanti devono richiedere blocchi di dati da 512 byte.
- Archiviazione DeviceProtocolSpecificProperty
Usare uno di questi ID di proprietà specifici del protocollo in combinazione con STORAGE_PROTOCOL_SPECIFIC_DATA per recuperare dati specifici del protocollo nella struttura STORAGE_PROTOCOL_DATA_DESCRIPTOR.
- Archiviazione AdapterTemperatureProperty
- Archiviazione DeviceTemperatureProperty
Usare uno di questi ID di proprietà temperature per recuperare i dati relativi alla temperatura nella struttura STORAGE_TEMPERATURE_DATA_DESCRIPTOR.
STORAGE_PROTOCOL_SPECIFIC_DATA : recuperare dati specifici di NVMe quando questa struttura viene usata per il campo AdditionalParameters di STORAGE_PROPERTY_QUERY e viene specificato un valore di enumerazione STORAGE_PROTOCOL_NVME_DATA_TYPE. Utilizzare uno dei valori di STORAGE_PROTOCOL_NVME_DATA_TYPE seguenti nel campo DataType della struttura STORAGE_PROTOCOL_SPECIFIC_DATA:
- Usare NVMeDataTypeIdentify per ottenere i dati del controller di identificazione o identificare i dati dello spazio dei nomi.
- Usare NVMeDataTypeLogPage per ottenere pagine di log (inclusi i dati smart/integrità).
- Usare NVMeDataTypeFeature per ottenere le funzionalità dell'unità NVMe.
STORAGE_TEMPERATURE_INFO : questa struttura viene usata per contenere dati di temperatura specifici. Viene usato nella STORAGE_TEMERATURE_DATA_DESCRIPTOR per restituire i risultati di una query sulla temperatura.
IOCTL_STORAGE_edizione StandardT_TEMPERATURE_THRESHOLD : usare questo IOCTL con la struttura STORAGE_TEMPERATURE_THRESHOLD per impostare le soglie di temperatura. Per altre info, vedi Modifica del comportamento dei comandi.
STORAGE_TEMPERATURE_THRESHOLD : questa struttura viene usata come buffer di input per specificare la soglia di temperatura. Il campo OverThreshold (boolean) specifica se il campo Soglia è il valore di soglia superiore o meno (in caso contrario, è il valore inferiore alla soglia).
Meccanismo pass-through
I comandi non definiti nella specifica NVMe sono i più difficili da gestire per il sistema operativo host: l'host non ha informazioni dettagliate sugli effetti che i comandi possono avere nel dispositivo di destinazione, l'infrastruttura esposta (spazi dei nomi/dimensioni dei blocchi) e il relativo comportamento.
Per trasportare meglio questi comandi specifici del dispositivo tramite lo stack di archiviazione di Windows, un nuovo meccanismo pass-through consente l'invio tramite pipe di comandi specifici del fornitore. Questa pipe pass-through consente anche lo sviluppo di strumenti di gestione e test. Tuttavia, questo meccanismo pass-through richiede l'uso del log degli effetti del comando. Inoltre, StoreNVMe.sys richiede tutti i comandi, non solo i comandi pass-through, da descrivere nel log degli effetti del comando.
Importante
StorNVMe.sys e Storport.sys bloccano qualsiasi comando in un dispositivo se non è descritto nel log degli effetti del comando.
Supporto del log degli effetti del comando
Il log degli effetti del comando (come descritto in Comandi supportati ed effetti, sezione 5.10.1.5 della specifica NVMe 1.2) consente la descrizione degli effetti dei comandi specifici del fornitore insieme ai comandi definiti dalla specifica. Ciò facilita sia il supporto della convalida dei comandi che l'ottimizzazione del comportamento dei comandi e pertanto deve essere implementato per l'intero set di comandi supportato dal dispositivo. Le condizioni seguenti descrivono il risultato sulla modalità di invio del comando in base alla voce Log effetti comando.
Per qualsiasi comando specifico descritto nel log degli effetti del comando...
While:
Comando supportato (CSUPP) è impostato su '1' che indica che il comando è supportato dal controller (Bit 01)
Nota
Quando CSUPP è impostato su '0' (che indica che il comando non è supportato) il comando verrà bloccato
E se viene impostata una delle opzioni seguenti:
La modifica delle funzionalità del controller (CCC) è impostata su '1' che indica che il comando può modificare le funzionalità del controller (Bit 04)
La modifica dell'inventario dello spazio dei nomi (NIC) è impostata su '1' che indica che il comando può modificare il numero o le funzionalità per più spazi dei nomi (Bit 03)
La modifica della funzionalità dello spazio dei nomi (NCC) è impostata su '1' che indica che il comando può modificare le funzionalità di un singolo spazio dei nomi (Bit 02)
Invio ed esecuzione dei comandi (C edizione Standard) è impostato su 001b o 010b, che indica che il comando può essere inviato quando non è presente un altro comando in sospeso allo stesso o a uno spazio dei nomi e che un altro comando non deve essere inviato allo stesso o ad alcuno spazio dei nomi fino al completamento di questo comando (bit 18:16)
Il comando verrà quindi inviato come unico comando in sospeso all'adapter.
Else if:
- Invio ed esecuzione dei comandi (C edizione Standard) è impostato su 001b, che indica che il comando può essere inviato quando non è presente un altro comando in sospeso allo stesso spazio dei nomi e che un altro comando non deve essere inviato allo stesso spazio dei nomi finché questo comando non viene completato (bit 18:16)
Il comando verrà quindi inviato come unico comando in sospeso all'oggetto Numero unità logica (LUN).
In caso contrario, il comando viene inviato con altri comandi in sospeso senza inibire. Ad esempio, se un comando specifico del fornitore viene inviato al dispositivo per recuperare informazioni statistiche non definite da specifiche, non dovrebbe esserci alcun rischio di modificare il comportamento o la funzionalità del dispositivo per eseguire i comandi di I/O. Tali richieste potrebbero essere gestite in parallelo all'I/O e non sarebbe necessario sospendere la ripresa.
Uso di IOCTL_STORAGE_PROTOCOL_COMMAND per inviare comandi
Il pass-through può essere eseguito usando il IOCTL_STORAGE_PROTOCOL_COMMAND, introdotto in Windows 10. Questo IOCTL è stato progettato per avere un comportamento simile a quello degli IOCTL SCSI e ATA esistenti, per inviare un comando incorporato al dispositivo di destinazione. Tramite questo IOCTL, il pass-through può essere inviato a un dispositivo di archiviazione, inclusa un'unità NVMe.
Ad esempio, in NVMe, IOCTL consentirà l'invio dei codici di comando seguenti.
- Comandi Amministrazione specifici del fornitore (C0h - FFh)
- Comandi NVMe specifici del fornitore (80h - FFh)
Come per tutti gli altri IOCTLs, usare DeviceIoControl per inviare il valore IOCTL pass-through. L'IOCTL viene popolato usando la struttura del buffer di input STORAGE_PROTOCOL_COMMAND presente in ntddstor.h. Popolare il campo Comando con il comando specifico del fornitore.
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;
Il comando specifico del fornitore da inviare deve essere popolato nel campo evidenziato sopra. Si noti di nuovo che il log degli effetti del comando deve essere implementato per i comandi pass-through. In particolare, questi comandi devono essere segnalati come supportati nel log degli effetti del comando (vedere la sezione precedente per altre informazioni). Si noti anche che i campi PRP sono specifici del driver, pertanto le applicazioni che inviano comandi possono lasciarli come 0.
Infine, questo IOCTL pass-through è destinato all'invio di comandi specifici del fornitore. Per inviare altri comandi NVMe specifici dell'amministratore o non del fornitore, ad esempio Identificazione, non è consigliabile usare questo IOCTL pass-through. Ad esempio, IOCTL_STORAGE_QUERY_PROPERTY deve essere usato per identificare o ottenere pagine di log. Per altre informazioni, vedere la sezione successiva Query specifiche del protocollo.
Non aggiornare il firmware tramite il meccanismo pass-through
I comandi di download e attivazione del firmware non devono essere inviati tramite pass-through. IOCTL_STORAGE_PROTOCOL_COMMAND devono essere usati solo per i comandi specifici del fornitore.
Usare invece i IOCTL di archiviazione generali seguenti (introdotti in Windows 10) per evitare che le applicazioni usino direttamente la versione SCSI_miniport di Firmware IOCTL. Archiviazione driver convertono IOCTL in un comando SCSI o nella versione SCSI_miniport di IOCTL nel miniport.
Questi IOCTLs sono consigliati per lo sviluppo di strumenti di aggiornamento del firmware in Windows 10 e Windows Server 2016:
Per ottenere informazioni di archiviazione e aggiornare il firmware, Windows supporta anche i cmdlet di PowerShell per eseguire questa operazione rapidamente:
Get-StorageFirmwareInfo
Update-StorageFirmware
Nota
Per aggiornare il firmware in NVMe in Windows 8.1, usare IOCTL_SCSI_MINIPORT_FIRMWARE. Questo IOCTL non è stato sottoposto a backporting in Windows 7. Per altre informazioni, vedere Aggiornamento del firmware per un dispositivo NVMe in Windows 8.1.
Restituzione di errori tramite il meccanismo pass-through
Analogamente a IOCTL SCSI e ATA, quando un comando o una richiesta viene inviata al miniport o al dispositivo, L'IOCTL restituisce se ha avuto esito positivo o negativo. Nella struttura STORAGE_PROTOCOL_COMMAND, IOCTL restituisce lo stato tramite il campo ReturnStatus.
Esempio: invio di un comando specifico del fornitore
In questo esempio, un comando arbitrario specifico del fornitore (0xFF) viene inviato tramite pass-through a un'unità NVMe. Il codice seguente alloca un buffer, inizializza una query e quindi invia il comando al dispositivo tramite DeviceIoControl.
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 questo esempio, ci aspettiamo protocolCommand->ReturnStatus == STORAGE_PROTOCOL_STATUS_SUCCESS
se il comando è riuscito al dispositivo.
Query specifiche del protocollo
Windows 8.1 ha introdotto IOCTL_STORAGE_QUERY_PROPERTY per il recupero dei dati. In Windows 10, L'IOCTL è stato migliorato per supportare le funzionalità NVMe richieste comunemente, ad esempio Get Log Pages, Get Features e Identify. Ciò consente il recupero di informazioni specifiche NVMe a scopo di monitoraggio e inventario.
Il buffer di input per IOCTL, STORAGE_PROPERTY_QUERY (da Windows 10) è illustrato qui.
typedef struct _STORAGE_PROPERTY_QUERY {
STORAGE_PROPERTY_ID PropertyId;
STORAGE_QUERY_TYPE QueryType;
UCHAR AdditionalParameters[1];
} STORAGE_PROPERTY_QUERY, *PSTORAGE_PROPERTY_QUERY;
Quando si usa IOCTL_STORAGE_QUERY_PROPERTY per recuperare informazioni specifiche del protocollo NVMe nel STORAGE_PROTOCOL_DATA_DESCRIPTOR, configurare la struttura STORAGE_PROPERTY_QUERY come indicato di seguito:
Allocare un buffer che può contenere sia un STORAGE_PROPERTY_QUERY che una struttura STORAGE_PROTOCOL_SPECIFIC_DATA.
Impostare il campo PropertyID su Archiviazione AdapterProtocolSpecificProperty o Archiviazione DeviceProtocolSpecificProperty rispettivamente per una richiesta controller o dispositivo/spazio dei nomi.
Impostare il campo QueryType su PropertyStandardQuery.
Compilare la struttura STORAGE_PROTOCOL_SPECIFIC_DATA con i valori desiderati. L'inizio della STORAGE_PROTOCOL_SPECIFIC_DATA è il campo AdditionalParameters di STORAGE_PROPERTY_QUERY.
La struttura STORAGE_PROTOCOL_SPECIFIC_DATA (da Windows 10) è illustrata qui.
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;
Per specificare un tipo di informazioni specifiche del protocollo NVMe, configurare la struttura STORAGE_PROTOCOL_SPECIFIC_DATA come indicato di seguito:
Impostare il campo ProtocolType su ProtocolTypeNVMe.
Impostare il campo DataType su un valore di enumerazione definito da STORAGE_PROTOCOL_NVME_DATA_TYPE:
- Usare NVMeDataTypeIdentify per ottenere i dati del controller di identificazione o identificare i dati dello spazio dei nomi.
- Usare NVMeDataTypeLogPage per ottenere pagine di log (inclusi i dati smart/integrità).
- Usare NVMeDataTypeFeature per ottenere le funzionalità dell'unità NVMe.
Quando ProtocolTypeNVMe viene usato come ProtocolType, le query per informazioni specifiche del protocollo possono essere recuperate in parallelo con altre operazioni di I/O nell'unità NVMe.
Importante
Per un IOCTL_STORAGE_QUERY_PROPERTY che usa un STORAGE_PROPERTY_ID di Archiviazione AdapterProtocolSpecificProperty e la cui struttura STORAGE_PROTOCOL_SPECIFIC_DATA o STORAGE_PROTOCOL_SPECIFIC_DATA_EXT è impostata su ProtocolType=ProtocolTypeNvme
e DataType=NVMeDataTypeLogPage
, impostare il membro ProtocolDataLength della stessa struttura su un valore minimo di 512 (byte).
Gli esempi seguenti illustrano query specifiche del protocollo NVMe.
Esempio: query NVMe Identify
In questo esempio la richiesta Di identificazione viene inviata a un'unità NVMe. Il codice seguente inizializza la struttura dei dati di query e quindi invia il comando al dispositivo tramite DeviceIoControl.
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"));
}
}
Importante
Per un IOCTL_STORAGE_QUERY_PROPERTY che usa un STORAGE_PROPERTY_ID di Archiviazione AdapterProtocolSpecificProperty e la cui struttura STORAGE_PROTOCOL_SPECIFIC_DATA o STORAGE_PROTOCOL_SPECIFIC_DATA_EXT è impostata su ProtocolType=ProtocolTypeNvme
e DataType=NVMeDataTypeLogPage
, impostare il membro ProtocolDataLength della stessa struttura su un valore minimo di 512 (byte).
Si noti che il chiamante deve allocare un singolo buffer contenente STORAGE_PROPERTY_QUERY e le dimensioni di STORAGE_PROTOCOL_SPECIFIC_DATA. In questo esempio viene usato lo stesso buffer per l'input e l'output della query della proprietà. Ecco perché il buffer allocato ha una dimensione "FIELD_OFFedizione Standard T(STORAGE_PROPERTY_QUERY, AdditionalParameters) + sizeof(STORAGE_PROTOCOL_SPECIFIC_DATA) + NVME_MAX_LOG_SIZE". Anche se è possibile allocare buffer separati sia per l'input che per l'output, è consigliabile usare un singolo buffer per eseguire query sulle informazioni correlate a NVMe.
identifyControllerData-NN> è Number of Namespaces (NN). Windows rileva uno spazio dei nomi come unità fisica.
Esempio: query NVMe Get Log Pages
In questo esempio, in base a quello precedente, la richiesta Get Log Pages viene inviata a un'unità NVMe. Il codice seguente prepara la struttura dei dati di query e quindi invia il comando al dispositivo tramite DeviceIoControl.
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"));
}
I chiamanti possono usare un STORAGE_PROPERTY_ID di Archiviazione AdapterProtocolSpecificProperty e la cui struttura STORAGE_PROTOCOL_SPECIFIC_DATA o STORAGE_PROTOCOL_SPECIFIC_DATA_EXT è impostata su per ProtocolDataRequestValue=VENDOR_SPECIFIC_LOG_PAGE_IDENTIFIER
richiedere blocchi di byte di 512 byte di dati specifici del fornitore.
Esempio: query NVMe Get Features
In questo esempio, in base a quello precedente, la richiesta Get Features viene inviata a un'unità NVMe. Il codice seguente prepara la struttura dei dati di query e quindi invia il comando al dispositivo tramite DeviceIoControl.
//
// 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"));
}
Set specifico del protocollo
Da Windows 10 19H1, il IOCTL_STORAGE_edizione StandardT_PROPERTY è stato migliorato per supportare le funzionalità del set NVMe.
Il buffer di input per il IOCTL_STORAGE_edizione StandardT_PROPERTY è illustrato di seguito:
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;
Quando si usa IOCTL_STORAGE_edizione StandardT_PROPERTY per impostare la funzionalità NVMe, configurare la struttura STORAGE_PROPERTY_edizione Standard T come indicato di seguito:
- Allocare un buffer che può contenere sia un STORAGE_PROPERTY_edizione Standard T che una struttura STORAGE_PROTOCOL_SPECIFIC_DATA_EXT;
- Impostare il campo PropertyID su Archiviazione AdapterProtocolSpecificProperty o Archiviazione DeviceProtocolSpecificProperty rispettivamente per una richiesta controller o dispositivo/spazio dei nomi.
- Compilare la struttura STORAGE_PROTOCOL_SPECIFIC_DATA_EXT con i valori desiderati. L'inizio della STORAGE_PROTOCOL_SPECIFIC_DATA_EXT è il campo AdditionalParameters di STORAGE_PROPERTY_edizione Standard T.
La struttura STORAGE_PROTOCOL_SPECIFIC_DATA_EXT è illustrata qui.
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;
Per specificare un tipo di funzionalità NVMe da impostare, configurare la struttura STORAGE_PROTOCOL_SPECIFIC_DATA_EXT come indicato di seguito:
- Impostare il campo ProtocolType su ProtocolTypeNvme;
- Impostare il campo DataType sul valore di enumerazione NVMeDataTypeFeature definito da STORAGE_PROTOCOL_NVME_DATA_TYPE;
Gli esempi seguenti illustrano il set di funzionalità NVMe.
Esempio: Funzionalità del set NVMe
In questo esempio la richiesta Imposta funzionalità viene inviata a un'unità NVMe. Il codice seguente prepara la struttura dei dati del set e quindi invia il comando al dispositivo tramite DeviceIoControl.
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
);
Query sulla temperatura
In Windows 10 è anche possibile usare IOCTL_STORAGE_QUERY_PROPERTY per eseguire query sui dati relativi alla temperatura dai dispositivi NVMe.
Per recuperare informazioni sulla temperatura da un'unità NVMe nella STORAGE_TEMPERATURE_DATA_DESCRIPTOR, configurare la struttura STORAGE_PROPERTY_QUERY come indicato di seguito:
Allocare un buffer che può contenere una struttura STORAGE_PROPERTY_QUERY.
Impostare il campo PropertyID su Archiviazione AdapterTemperatureProperty o Archiviazione DeviceTemperatureProperty rispettivamente per una richiesta controller o dispositivo/spazio dei nomi.
Impostare il campo QueryType su PropertyStandardQuery.
La struttura STORAGE_TEMPERATURE_INFO (da Windows 10) è illustrata qui.
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;
Comandi di modifica del comportamento
I comandi che modificano gli attributi del dispositivo o influiscono potenzialmente sul comportamento del dispositivo sono più difficili da gestire per il sistema operativo. Se gli attributi del dispositivo cambiano in fase di esecuzione durante l'elaborazione dell'I/O, possono verificarsi problemi di sincronizzazione o integrità dei dati se non gestiti correttamente.
Il comando NVMe Set-Features è un buon esempio di comando di modifica del comportamento. Consente la modifica del meccanismo di arbitrato e l'impostazione delle soglie di temperatura. Per assicurarsi che i dati in anteprima non siano a rischio quando i comandi dei set che influiscono sul comportamento vengono inviati inattivi, Windows sospende tutte le operazioni di I/O sul dispositivo NVMe, svuota le code e scarica i buffer. Dopo che il comando set è stato eseguito correttamente, l'I/O viene ripreso (se possibile). Se non è possibile riprendere l'I/O, potrebbe essere necessaria una reimpostazione del dispositivo.
Impostazione delle soglie di temperatura
Windows 10 ha introdotto IOCTL_STORAGE_edizione StandardT_TEMPERATURE_THRESHOLD, un IOCTL per ottenere e impostare soglie di temperatura. È anche possibile usarlo per ottenere la temperatura corrente del dispositivo. Il buffer di input/output per questo IOCTL è la struttura STORAGE_TEMPERATURE_INFO , della sezione del codice precedente.
Esempio: Impostazione della temperatura di soglia eccessiva
In questo esempio viene impostata una temperatura superiore alla soglia di un'unità NVMe. Il codice seguente prepara il comando e quindi lo invia al dispositivo tramite DeviceIoControl.
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
);
Impostazione delle funzionalità specifiche del fornitore
Senza il log degli effetti del comando, il driver non ha alcuna conoscenza delle ramificazioni del comando. Questo è il motivo per cui è necessario il log degli effetti dei comandi. Aiuta il sistema operativo a determinare se un comando ha un impatto elevato e se può essere inviato in parallelo con altri comandi all'unità.
Il log degli effetti del comando non è ancora abbastanza granulare per includere i comandi set-features specifici del fornitore. Per questo motivo, non è ancora possibile inviare comandi Set-Features specifici del fornitore. Tuttavia, è possibile usare il meccanismo pass-through, descritto in precedenza, per inviare comandi specifici del fornitore. Per altre info, vedi Meccanismo pass-through.
File di intestazione
I file seguenti sono rilevanti per lo sviluppo NVMe. Questi file sono inclusi in Microsoft Windows Software Development Kit (SDK).
File di intestazione | Descrizione |
---|---|
ntddstor.h | Definisce costanti e tipi per l'accesso ai driver della classe di archiviazione dalla modalità kernel. |
nvme.h | Per altre strutture di dati correlate a NVMe. |
winioctl.h | Per le definizioni IOCTL Win32 complessive, incluse le API di archiviazione per le applicazioni in modalità utente. |