Megosztás a következőn keresztül:


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:

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=NVMeDataTypeLogPagevan 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=NVMeDataTypeLogPagevan 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.