NVMe-meghajtók használata
A következőkre vonatkozik:
- Windows 10
- Windows Server 2016
Megtudhatja, hogyan dolgozhat nagy sebességű NVMe-eszközökkel a Windows-alkalmazásból. Az eszközhozzáférés a StorNVMe.syshasználatával engedélyezett, amely először a Windows Server 2012 R2-ben és a Windows 8.1-ben megjelent alapértelmezett meghajtó. A Windows 7-eszközök számára is elérhető egy gyorsjavítással. A Windows 10-ben számos új funkció jelent meg, köztük egy átmenő mechanizmus a szállítóspecifikus NVMe-parancsokhoz és a meglévő IOCTL-ek frissítéséhez.
Ez a témakör áttekintést nyújt a Windows 10 NVMe-meghajtóinak eléréséhez használható általános használatú API-król. A cikk a következőket is ismerteti:
- Szállítóspecifikus NVMe-parancs küldése átvitellel
- Az azonosító küldése, szolgáltatások lekérése vagy naplólapok lekérése parancs elküldése az NVMe-meghajtóra
- Hőmérsékleti adatok lekérése egy NVMe-meghajtóról
- Viselkedést módosító parancsok végrehajtása, például hőmérsékleti küszöbértékek beállítása
API-k NVMe-meghajtókhoz
Az alábbi általánosan használt API-k segítségével érheti el az NVMe-meghajtókat a Windows 10-ben. Ezek az API-k winioctl.h találhatók felhasználói módú alkalmazásokhoz, és ntddstor.h kernel módú illesztőprogramokhoz. További információ a fejlécfájlokról: Fejlécfájlok.
IOCTL_STORAGE_PROTOCOL_COMMAND: Használja ezt az IOCTL-t a STORAGE_PROTOCOL_COMMAND struktúrával az NVMe-parancsok kiadásához. Ez az IOCTL lehetővé teszi az NVMe-átvitelt, és támogatja az NVMe parancshatások naplóját. Szállítóspecifikus parancsokkal is használhatja. További információ: Átmenő mechanizmus.
STORAGE_PROTOCOL_COMMAND: Ez a bemeneti pufferstruktúra tartalmaz egy ReturnStatus mezőt, amely az alábbi állapotértékek jelentésére használható.
- STORAGE_PROTOCOL_STATUS_PENDING
- TÁRHELY_PROTOKOLL_STÁTUSZ_SIKERES
- TÁROLÓ_PROTOKOLL_ÁLLAPOT_HIBA
- STORAGE_PROTOCOL_STATUS_INVALID_REQUEST
- STORAGE_PROTOCOL_STATUS_NO_DEVICE
- Tároló protokoll állapot: Foglalt (STORAGE_PROTOCOL_STATUS_BUSY)
- TÁRHELY_PROTOCOL_ÁLLAPOT_ADATHALMOZÓDÁS
- TÁRHELY_PROTOKOLL_ÁLLAPOT_ERŐFORRÁSOK_ELÉGTELENEK
- STORAGE_PROTOCOL_STATUS_NOT_SUPPORTED
IOCTL_STORAGE_QUERY_PROPERTY: Használja ezt az IOCTL-t a STORAGE_PROPERTY_QUERY szerkezettel az eszközinformációk lekéréséhez. További információ: Protokollspecifikus lekérdezések és Hőmérséklet-lekérdezések.
STORAGE_PROPERTY_QUERY: Ez a struktúra tartalmazza a PropertyId és AdditionalParameters mezőket a lekérdezendő adatok megadásához. A PropertyId mezőben a STORAGE_PROPERTY_ID felsorolás használatával adja meg az adatok típusát. Az AdditionalParameters mezővel további részleteket adhat meg az adatok típusától függően. Protokollspecifikus adatok esetén használja az AdditionalParameters mező STORAGE_PROTOCOL_SPECIFIC_DATA szerkezetét. A hőmérsékleti adatokhoz használja az AdditionalParameters mező STORAGE_TEMPERATURE_INFO szerkezetét.
STORAGE_PROPERTY_ID: Ez az enumerálás új értékeket tartalmaz, amelyek lehetővé teszik IOCTL_STORAGE_QUERY_PROPERTY protokollspecifikus és hőmérsékleti információk lekérését.
- StorageAdapterProtocolSpecificProperty: Ha ProtocolType = ProtocolTypeNvme és DataType = NVMeDataTypeLogPage, a hívóknak 512 bájt adattömböt kell kérniük.
- StorageDeviceProtocolSpecificProperty
A protokollspecifikus tulajdonságazonosítók és a STORAGE_PROTOCOL_SPECIFIC_DATA együttes használatával kérje le a protokollspecifikus adatokat a STORAGE_PROTOCOL_DATA_DESCRIPTOR struktúrában.
- TárolóAdapterHőmérsékletTulajdonság
- TárolóeszközHőmérsékletiTulajdonság
Ezen hőmérséklettulajdonság-azonosítók egyikével lekérheti a hőmérsékletadatokat a STORAGE_TEMPERATURE_DATA_DESCRIPTOR struktúrában.
STORAGE_PROTOCOL_SPECIFIC_DATA: Az NVMe-specifikus adatok lekérése, ha ezt a struktúrát a STORAGE_PROPERTY_QUERY AdditionalParameters mezőhöz használják, és STORAGE_PROTOCOL_NVME_DATA_TYPE enumerálási érték van megadva. Használja az alábbi STORAGE_PROTOCOL_NVME_DATA_TYPE értékek egyikét a STORAGE_PROTOCOL_SPECIFIC_DATA struktúra DataType mezőjében:
- Az NVMeDataTypeIdentify használatával lekérheti az azonosító vezérlő adatait vagy a névtéradatokat.
- Az NVMeDataTypeLogPage használatával kérje le a naplólapokat (beleértve a SMART/health adatokat is).
- Az NVMe-meghajtó funkcióinak lekéréséhez használja NVMeDataTypeFeature.
STORAGE_TEMPERATURE_INFO: Ez a szerkezet adott hőmérsékleti adatok tárolására szolgál. A STORAGE_TEMERATURE_DATA_DESCRIPTOR egy hőmérséklet-lekérdezés eredményeinek visszaadására szolgál.
IOCTL_STORAGE_SET_TEMPERATURE_THRESHOLD: Használja ezt az IOCTL-t a STORAGE_TEMPERATURE_THRESHOLD szerkezettel a hőmérsékleti küszöbértékek beállításához. További információ: A parancsok viselkedésének módosítása.
STORAGE_TEMPERATURE_THRESHOLD: A rendszer bemeneti pufferként használja ezt a struktúrát a hőmérséklet küszöbértékének meghatározásához. Az OverThreshold mező (logikai érték) azt határozza meg, hogy a Küszöbérték mező eléri-e, vagy meghaladja-e a küszöbértéket (ellenkező esetben a küszöbérték alatti értékről van szó).
Átmenő mechanizmus
Az NVMe specifikációban nem definiált parancsok kezelése a legnehezebb a gazdagép operációs rendszerének, mivel a gazdagép nem látja át, milyen hatással vannak ezek a parancsok a céleszközre, a közzétett infrastruktúrára (névterek/blokkméretek) és annak viselkedésére.
Az ilyen eszközspecifikus parancsok Windows-tárolóveremen keresztüli jobb átvitele érdekében egy új átmenő mechanizmus lehetővé teszi a szállítóspecifikus parancsok átvitelét. Ez az átmenő cső a felügyeleti és tesztelési eszközök fejlesztését is segíti. Ehhez az átmenő mechanizmushoz azonban a parancseffektusok naplóját kell használni. Ezenkívül StoreNVMe.sys a parancseffektusok naplójában nem csak az átmenő parancsokat, hanem az összes parancsot is le kell írni.
Fontos
StorNVMe.sys és Storport.sys letiltja az eszköz parancsait, ha az nem szerepel a parancseffektusok naplójában.
A parancseffektusok naplójának támogatása
A parancseffektusok naplója (a Támogatott parancsok és effektusok szakaszban leírtak szerint, NVMe Specification 1.25.10.1.5 szakasza) lehetővé teszi a gyártóspecifikus parancsok és a specifikáció által meghatározott parancsok hatásainak leírását. Ez megkönnyíti a parancsok érvényesítését és a parancsok viselkedésének optimalizálását, ezért az eszköz által támogatott parancsok teljes halmazára implementálandó. Az alábbi feltételek a parancs elküldési módjának eredményét írják le a parancseffektusok naplóbejegyzése alapján.
A Parancsok Hatásainak Naplójában leírt bármely konkrét parancs esetében...
Míg:
A támogatott parancs (CSUPP) értéke "1", ami azt jelzi, hogy a vezérlő támogatja a parancsot (01. bit)
Jegyzet
Ha a CSUPP értéke "0" (azt jelzi, hogy a parancs nem támogatott), a parancs le lesz tiltva
És ha az alábbiak bármelyike be van állítva:
A vezérlő képességeinek módosítása (CCC) 1 értékre van állítva, ami azt jelzi, hogy a parancs megváltoztathatja a vezérlő képességeit (04. bit)
A névtérleltár módosítása (NIC) értéke "1", ami azt jelzi, hogy a parancs megváltoztathatja a több névtér számát vagy képességeit (03. bit)
A névtér képességének módosítása (NCC) értéke "1", ami azt jelzi, hogy a parancs megváltoztathatja egy névtér képességeit (02. bit)
A parancsbeküldés és -végrehajtás (CSE) értéke 001b vagy 010b, ami azt jelzi, hogy a parancs elküldhető, ha nincs más kiugró parancs ugyanahhoz vagy névtérhez, és hogy egy másik parancs nem küldhető el ugyanahhoz vagy névtérhez mindaddig, amíg a parancs be nem fejeződik (Bits 18:16)
Ezután a parancsot küldik el az egyetlen függőben lévő parancsként az adapternek.
Máskülönben ha:
- A parancsküldés és -végrehajtás (CSE) értéke 001b, ami azt jelzi, hogy a parancs akkor küldhető el, ha nincs más függőben lévő parancs ugyanahhoz a névtérhez, és hogy egy másik parancs nem küldhető el ugyanahhoz a névtérhez, amíg a parancs be nem fejeződik (Bits 18:16)
Ezután a parancs lesz az egyetlen kiugró parancs a logikaiegység-szám objektum (LUN) számára.
Ellenkező esetben, a parancsot gátlás nélkül küldi el a rendszer az egyéb parancsokkal együtt. Ha például egy szállítóspecifikus parancsot küldenek az eszköznek a nem spec-definiált statisztikai adatok lekéréséhez, nem lehet kockázat az eszköz viselkedésének vagy az I/O-parancsok végrehajtására való képességének megváltoztatására. Az ilyen kérések az I/O-kkal párhuzamosan is kiszolgálhatók, és nem lenne szükség szüneteltetési folytatásra.
Parancsok küldése IOCTL_STORAGE_PROTOCOL_COMMAND használatával
A továbbítás a Windows 10-ben bevezetett IOCTL_STORAGE_PROTOCOL_COMMANDhasználatával végezhető el. Ezt az IOCTL-t úgy tervezték, hogy a meglévő SCSI- és ATA-átmenő IOCTL-ekhez hasonlóan viselkedjen, hogy beágyazott parancsot küldjön a céleszközre. Ezen az IOCTL utasításon keresztül továbbítás végezhető egy tárolóeszközre, beleértve egy NVMe-meghajtót is.
Az NVMe-ben például az IOCTL engedélyezi a következő parancskódok küldését.
- Szállítóspecifikus rendszergazdai parancsok (C0h – FFh)
- Szállítóspecifikus NVMe-parancsok (80h – FFh)
Az összes többi IOCTL-hez hasonlóan használja a DeviceIoControl parancsot az átmenő IOCTL elküldéséhez. Az IOCTL a STORAGE_PROTOCOL_COMMAND bemeneti-puffer szerkezete segítségével van feltöltve, amely a ntddstor.hfájlban található. Töltse ki a parancs mezőt a gyártóspecifikus paranccsal.
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;
Az elküldendő szállítóspecifikus parancsot a fenti kiemelt mezőben kell kitölteni. Vegye figyelembe, hogy a parancseffektusok naplójának implementálva kell lennie az átmenő parancsokhoz. Ezeket a parancsokat különösen a parancseffektusok naplójában támogatottként kell jelenteni (további információkért lásd az előző szakaszt). Azt is vegye figyelembe, hogy a PRP-mezők illesztőprogram-specifikusak, így a parancsokat küldő alkalmazások 0-ként hagyhatják őket.
Végül ez az átmenő IOCTL szállítóspecifikus parancsok küldésére szolgál. Más rendszergazdai vagy nem szállítóspecifikus NVMe-parancsok( például az Azonosítás) küldéséhez ez az átmenő IOCTL nem használható. A naplólapok azonosításához vagy lekéréséhez például IOCTL_STORAGE_QUERY_PROPERTY kell használni. További információkért lásd a következő szakaszt, protokollspecifikus lekérdezéseket.
Ne frissítse a belső vezérlőprogramot az átmenő mechanizmuson keresztül
A belső vezérlőprogram letöltési és aktiválási parancsai nem küldhetők átmenő paranccsal. IOCTL_STORAGE_PROTOCOL_COMMAND csak szállítóspecifikus parancsokhoz használható.
Ehelyett használja a következő általános tárolási IOCTL-eket (amelyeket a Windows 10-ben vezettek be), hogy elkerülje azt, hogy az alkalmazások közvetlenül a Firmware IOCTL SCSI_miniport verzióját használják. A tárolóillesztők az IOCTL-t SCSI-parancsra vagy az IOCTL SCSI_miniport verziójára fordítják le a miniportra.
Ezeket az IOCTLs-eket javasoljuk a belső vezérlőprogram-frissítési eszközök fejlesztéséhez a Windows 10-ben és a Windows Server 2016-ban:
A tárolási információk lekéréséhez és a belső vezérlőprogram frissítéséhez a Windows a PowerShell-parancsmagokat is támogatja a gyors művelethez:
Get-StorageFirmwareInfo
Update-StorageFirmware
Jegyzet
A Windows 8.1 rendszerben az NVMe firmverének frissítéséhez a következő lépéseket követve használja az IOCTL_SCSI_MINIPORT_FIRMWARE-t. Ez az IOCTL nem lett visszajelentve a Windows 7-be. További információ: NVMe-eszköz belső vezérlőprogramjának frissítése Windows 8.1-ben.
Hibák visszaadása az átmenő mechanizmuson keresztül
Az SCSI-hez és az ATA-átmenő IOCTL-ekhez hasonlóan, ha a rendszer parancsot vagy kérést küld a miniportnak vagy az eszköznek, az IOCTL akkor tér vissza, ha az sikeres volt vagy nem. A STORAGE_PROTOCOL_COMMAND struktúrában az IOCTL az állapotot a ReturnStatus mezőn keresztül adja vissza.
Példa: szállítóspecifikus parancs küldése
Ebben a példában a rendszer egy tetszőleges szállítóspecifikus parancsot (0xFF) küld átmenően egy NVMe-meghajtóra. Az alábbi kód lefoglal egy puffert, inicializál egy lekérdezést, majd elküldi a parancsot az eszköznek a DeviceIoControlon keresztül.
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
);
Ebben a példában protocolCommand->ReturnStatus == STORAGE_PROTOCOL_STATUS_SUCCESS
-t várjuk, ha a parancs sikeresen teljesült az eszközön.
Protokollspecifikus lekérdezések
A(z) Windows 8.1 az adatlekéréshez vezette be a IOCTL_STORAGE_QUERY_PROPERTY-t. A Windows 10-ben az IOCTL-t bővítették, hogy támogassa a gyakran kért NVMe-funkciókat, például a Naplólapok lekérése, A szolgáltatások lekéréseés azonosítása. Ez lehetővé teszi az NVMe-specifikus információk lekérését monitorozási és leltározási célokra.
Itt látható az IOCTL bemeneti puffere, STORAGE_PROPERTY_QUERY (Windows 10-ből).
typedef struct _STORAGE_PROPERTY_QUERY {
STORAGE_PROPERTY_ID PropertyId;
STORAGE_QUERY_TYPE QueryType;
UCHAR AdditionalParameters[1];
} STORAGE_PROPERTY_QUERY, *PSTORAGE_PROPERTY_QUERY;
Ha IOCTL_STORAGE_QUERY_PROPERTY használ az NVMe protokollspecifikus információinak lekéréséhez a STORAGE_PROTOCOL_DATA_DESCRIPTOR, konfigurálja a STORAGE_PROPERTY_QUERY struktúrát az alábbiak szerint:
Egy olyan puffer lefoglalása, amely képes tartalmazni mind a STORAGE_PROPERTY_QUERY, mind pedig a STORAGE_PROTOCOL_SPECIFIC_DATA struktúrákat.
Állítsa be a PropertyID mezőt a StorageAdapterProtocolSpecificProperty-ra vagy a StorageDeviceProtocolSpecificProperty-re, vezérlő vagy eszköz/névtér kérelem esetén.
Állítsa a QueryType mezőt a PropertyStandardQueryértékre.
Töltse ki a STORAGE_PROTOCOL_SPECIFIC_DATA struktúrát a kívánt értékekkel. A STORAGE_PROTOCOL_SPECIFIC_DATA eleje a AdditionalParameters mezője a STORAGE_PROPERTY_QUERYrészből.
Itt látható a STORAGE_PROTOCOL_SPECIFIC_DATA struktúra (Windows 10-ből).
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;
Az NVMe protokollspecifikus információtípusának megadásához konfigurálja a STORAGE_PROTOCOL_SPECIFIC_DATA struktúrát az alábbiak szerint:
Állítsa a ProtocolType mezőt a ProtocolTypeNVMeértékre.
Állítsa a DataType mezőt a STORAGE_PROTOCOL_NVME_DATA_TYPEáltal meghatározott számbavételi értékre:
- Az NVMeDataTypeIdentify használatával lekérheti az azonosító vezérlő adatait vagy a névtéradatokat.
- Az NVMeDataTypeLogPage használatával kérje le a naplólapokat (beleértve a SMART/health adatokat is).
- Az NVMe-meghajtó funkcióinak lekéréséhez használja NVMeDataTypeFeature.
Ha a ProtocolTypeNVMe mint ProtocolTypekerül használatra, a protokollspecifikus információk lekérdezése párhuzamosan is történhet más I/O-műveletekkel az NVMe-meghajtón.
Fontos
Azon IOCTL_STORAGE_QUERY_PROPERTY esetében, amely StorageAdapterProtocolSpecificPropertySTORAGE_PROPERTY_ID használ, és amelynek STORAGE_PROTOCOL_SPECIFIC_DATA vagy STORAGE_PROTOCOL_SPECIFIC_DATA_EXT struktúrája ProtocolType=ProtocolTypeNvme
és DataType=NVMeDataTypeLogPage
van beállítva, állítsa az adott struktúra ProtocolDataLength tagját legalább 512 (bájt) értékre.
Az alábbi példák az NVMe protokollspecifikus lekérdezéseit mutatják be.
Példa: NVMe-azonosító lekérdezés
Ebben a példában a Azonosító kérést egy NVMe-meghajtóra küldi a rendszer. Az alábbi kód inicializálja a lekérdezési adatstruktúrát, majd elküldi a parancsot az eszköznek a DeviceIoControlon keresztül.
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"));
}
}
Fontos
Azon IOCTL_STORAGE_QUERY_PROPERTY esetében, amely StorageAdapterProtocolSpecificPropertySTORAGE_PROPERTY_ID használ, és amelynek STORAGE_PROTOCOL_SPECIFIC_DATA vagy STORAGE_PROTOCOL_SPECIFIC_DATA_EXT struktúrája ProtocolType=ProtocolTypeNvme
és DataType=NVMeDataTypeLogPage
van beállítva, állítsa az adott struktúra ProtocolDataLength tagját legalább 512 (bájt) értékre.
Vegye figyelembe, hogy a hívónak egyetlen, STORAGE_PROPERTY_QUERY és STORAGE_PROTOCOL_SPECIFIC_DATA méretű puffert kell lefoglalnia. Ebben a példában ugyanazt a puffert használja a tulajdonság-lekérdezés bemenetéhez és kimenetéhez. Ezért a lefoglalt puffer mérete "FIELD_OFFSET(STORAGE_PROPERTY_QUERY, AdditionalParameters) + sizeof(STORAGE_PROTOCOL_SPECIFIC_DATA) + NVME_MAX_LOG_SIZE". Bár a bemenethez és a kimenethez is külön puffereket lehet lefoglalni, javasoljuk, hogy egyetlen puffert használjon az NVMe kapcsolódó információinak lekérdezéséhez.
identifyControllerData->Az NN a névterek száma (NN). A Windows fizikai meghajtóként észleli a névteret.
Példa: NVMe naplólapok lekérdezése
Ebben a példában az előzőből kiindulva a rendszer elküldi a Naplólapok lekérése kérést egy NVMe-meghajtóra. Az alábbi kód előkészíti a lekérdezési adatstruktúrát, majd elküldi a parancsot az eszköznek a DeviceIoControlon keresztül.
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"));
}
A hívók használhatják a STORAGE_PROPERTY_IDStorageAdapterProtocolSpecificProperty, és amelynek STORAGE_PROTOCOL_SPECIFIC_DATA vagy STORAGE_PROTOCOL_SPECIFIC_DATA_EXT struktúrája ProtocolDataRequestValue=VENDOR_SPECIFIC_LOG_PAGE_IDENTIFIER
van beállítva, hogy 512 bájtos szolgáltatóspecifikus adattömböket kérjenek le.
Példa: NVMe Get Features lekérdezés
Ebben a példában az előzőből kiindulva a rendszer elküldi a Szolgáltatások lekérése kérést egy NVMe-meghajtóra. Az alábbi kód előkészíti a lekérdezési adatstruktúrát, majd elküldi a parancsot az eszköznek a DeviceIoControlon keresztül.
//
// 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"));
}
Protokollspecifikus készlet
A Windows 10 19H1 verziótól kezdve a IOCTL_STORAGE_SET_PROPERTY továbbfejlesztésre került az NVMe Set Features támogatásához.
A IOCTL_STORAGE_SET_PROPERTY bemeneti puffere itt látható:
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;
Ha IOCTL_STORAGE_SET_PROPERTY használ az NVMe-funkció beállításához, konfigurálja a STORAGE_PROPERTY_SET struktúrát az alábbiak szerint:
- Olyan puffer lefoglalása, amely STORAGE_PROPERTY_SET és STORAGE_PROTOCOL_SPECIFIC_DATA_EXT struktúrát is tartalmazhat;
- Állítsa a PropertyID mezőt StorageAdapterProtocolSpecificProperty vagy StorageDeviceProtocolSpecificProperty értékre egy vezérlő vagy eszköz/névtér kérés esetében.
- Töltse ki a STORAGE_PROTOCOL_SPECIFIC_DATA_EXT struktúrát a kívánt értékekkel. A STORAGE_PROTOCOL_SPECIFIC_DATA_EXT a STORAGE_PROPERTY_SET AdditionalParameters mezőjének eleje.
Itt látható a STORAGE_PROTOCOL_SPECIFIC_DATA_EXT struktúra.
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;
A beállítani kívánt NVMe-funkció típusának megadásához konfigurálja a STORAGE_PROTOCOL_SPECIFIC_DATA_EXT struktúrát az alábbiak szerint:
- Állítsa a ProtocolType mezőt ProtocolTypeNvme értékre;
- Állítsa a DataType mezőt a STORAGE_PROTOCOL_NVME_DATA_TYPE által definiált NVMeDataTypeFeature enumerálási értékre;
Az alábbi példák az NVMe szolgáltatáskészletét mutatják be.
Példa: NVMe beállítási funkciók
Ebben a példában a Rendszer elküldi a Szolgáltatások beállítása kérelmet egy NVMe-meghajtóra. Az alábbi kód előkészíti a beállított adatstruktúrát, majd elküldi a parancsot az eszköznek a DeviceIoControlon keresztül.
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
);
Hőmérséklet-lekérdezések
A Windows 10-ben a IOCTL_STORAGE_QUERY_PROPERTY az NVMe-eszközök hőmérsékleti adatainak lekérdezésére is használható.
Ha egy NVMe-meghajtó hőmérsékletadatait szeretné lekérni a STORAGE_TEMPERATURE_DATA_DESCRIPTOR, konfigurálja a STORAGE_PROPERTY_QUERY struktúrát az alábbiak szerint:
Foglaljon le egy puffert, amely tartalmazhat egy STORAGE_PROPERTY_QUERY struktúrát.
Állítsa be a PropertyID mezőt a StorageAdapterTemperatureProperty értékre vezérlő kérés esetén, vagy a StorageDeviceTemperatureProperty értékre eszköz/névtér kérés esetén.
Állítsa a QueryType mezőt a PropertyStandardQuery.
Itt látható a STORAGE_TEMPERATURE_INFO struktúra (Windows 10-ből).
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;
Viselkedést módosító parancsok
Az eszközattribútumokat módosító vagy az eszköz viselkedésére potenciálisan hatással lévő parancsokat nehezebb kezelni az operációs rendszer. Ha az eszközattribútumok futásidőben változnak az I/O feldolgozása során, szinkronizálási vagy adatintegritási problémák léphetnek fel, ha nem kezelik megfelelően.
Az NVMe Set-Features parancs jó példa a viselkedést módosító parancsra. Lehetővé teszi a választottbírósági mechanizmus módosítását és a hőmérsékleti küszöbértékek beállítását. Annak biztosítása érdekében, hogy a repülés közbeni adatok ne legyenek veszélyben a viselkedést befolyásoló parancsok elküldésekor, a Windows felfüggeszti az összes I/O-t az NVMe-eszközre, kiüríti az üzenetsorokat és kiüríti a puffereket. A beállított parancs sikeres végrehajtása után az I/O folytatódik (ha lehetséges). Ha az I/O nem folytatható, szükség lehet az eszköz alaphelyzetbe állítására.
Hőmérsékleti küszöbértékek beállítása
A Windows 10 bevezette a IOCTL_STORAGE_SET_TEMPERATURE_THRESHOLD, egy IOCTL-t a hőmérsékleti küszöbértékek lekéréséhez és beállításához. Az eszköz aktuális hőmérsékletének lekéréséhez is használhatja. Az IOCTL bemeneti/kimeneti puffere az előző kódszakaszból származó STORAGE_TEMPERATURE_INFO struktúra.
Példa: Küszöbérték feletti hőmérséklet beállítása
Ebben a példában egy NVMe-meghajtó küszöbérték feletti hőmérséklete van beállítva. Az alábbi kód előkészíti a parancsot, majd elküldi az eszközre a DeviceIoControlon keresztül.
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
);
Szállítóspecifikus funkciók beállítása
A parancseffektusok naplója nélkül az illesztőprogram nem ismeri a parancs következményeit. Ezért van szükség a parancshatások naplójára. Segít az operációs rendszernek meghatározni, hogy a parancsok nagy hatással vannak-e rá, és hogy a parancsok más parancsokkal párhuzamosan küldhetők-e a meghajtóra.
A parancseffektusok naplója még nem elég részletes ahhoz, hogy magában foglalja a gyártóspecifikus Set-Features parancsokat. Ezért még nem lehet szállítóspecifikus Set-Features parancsokat küldeni. A korábban tárgyalt átmenő mechanizmussal azonban szállítóspecifikus parancsokat küldhet. További információ: Átmenő mechanizmus.
Fejlécfájlok
Az alábbi fájlok az NVMe-fejlesztés szempontjából relevánsak. Ezek a fájlok a Microsoft Windows Software Development Kit (SDK)részét képezik.
Fejléc fájl | Leírás |
---|---|
ntddstor.h | Állandókat és típusokat határoz meg a tárolóosztály-illesztőprogramok kernel módból való eléréséhez. |
nvme.h | Egyéb NVMe-hez kapcsolódó adatstruktúrákhoz. |
winioctl.h | Az általános Win32 IOCTL-definíciók esetében, beleértve a felhasználói módú alkalmazások tárolási API-jait is. |