Bekerja dengan drive NVMe

Berlaku untuk:

  • Windows 10
  • Server Windows 2016

Pelajari cara bekerja dengan perangkat NVMe berkecepatan tinggi dari aplikasi Windows Anda. Akses perangkat diaktifkan melalui StorNVMe.sys, driver dalam kotak pertama kali diperkenalkan di Windows Server 2012 R2 dan Windows 8.1. Ini juga tersedia untuk perangkat Windows 7 melalui perbaikan panas KB. Di Windows 10, beberapa fitur baru diperkenalkan, termasuk mekanisme pass-through untuk perintah NVMe khusus vendor dan pembaruan untuk IOCTL yang ada.

Topik ini memberikan gambaran umum API penggunaan umum yang dapat Anda gunakan untuk mengakses drive NVMe di Windows 10. Ini juga menjelaskan:

API untuk bekerja dengan drive NVMe

Anda dapat menggunakan API penggunaan umum berikut untuk mengakses drive NVMe di Windows 10. API ini dapat ditemukan di winioctl.h untuk aplikasi mode pengguna, dan ntddstor.h untuk driver mode kernel. Untuk informasi selengkapnya tentang file header, lihat File header.

  • IOCTL_STORAGE_PROTOCOL_COMMAND : Gunakan IOCTL ini dengan struktur STORAGE_PROTOCOL_COMMAND untuk mengeluarkan perintah NVMe. IOCTL ini memungkinkan pass-through NVMe dan mendukung log Efek Perintah di NVMe. Anda dapat menggunakannya dengan perintah khusus vendor. Untuk informasi selengkapnya, lihat Mekanisme pass-through.

  • STORAGE_PROTOCOL_COMMAND : Struktur buffer input ini menyertakan bidang ReturnStatus yang dapat digunakan melaporkan nilai status berikut.

    • 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 : Gunakan IOCTL ini dengan struktur STORAGE_PROPERTY_QUERY untuk mengambil informasi perangkat. Untuk informasi selengkapnya, lihat Kueri khusus protokol dan Kueri suhu.

  • STORAGE_PROPERTY_QUERY : Struktur ini mencakup bidang PropertyId dan AdditionalParameters untuk menentukan data yang akan dikueri. Dalam PropertyId yang diajukan, gunakan enumerasi STORAGE_PROPERTY_ID untuk menentukan jenis data. Gunakan bidang AdditionalParameters untuk menentukan detail selengkapnya, tergantung pada jenis data. Untuk data khusus protokol, gunakan struktur STORAGE_PROTOCOL_SPECIFIC_DATA di bidang AdditionalParameters . Untuk data suhu, gunakan struktur STORAGE_TEMPERATURE_INFO di bidang AdditionalParameters .

  • STORAGE_PROPERTY_ID : Enumerasi ini mencakup nilai baru yang memungkinkan IOCTL_STORAGE_QUERY_PROPERTY mengambil informasi khusus protokol dan suhu.

    • StorageAdapterProtocolSpecificProperty: Jika ProtocolType = ProtocolTypeNvme dan DataType = NVMeDataTypeLogPage, pemanggil harus meminta potongan data 512 byte.
    • StorageDeviceProtocolSpecificProperty

    Gunakan salah satu ID properti khusus protokol ini dalam kombinasi dengan STORAGE_PROTOCOL_SPECIFIC_DATA untuk mengambil data khusus protokol dalam struktur STORAGE_PROTOCOL_DATA_DESCRIPTOR.

    • StorageAdapterTemperatureProperty
    • StorageDeviceTemperatureProperty

    Gunakan salah satu ID properti suhu ini untuk mengambil data suhu dalam struktur STORAGE_TEMPERATURE_DATA_DESCRIPTOR.

  • STORAGE_PROTOCOL_SPECIFIC_DATA : Ambil data khusus NVMe saat struktur ini digunakan untuk bidang AdditionalParameters STORAGE_PROPERTY_QUERYdan nilai enum STORAGE_PROTOCOL_NVME_DATA_TYPE ditentukan. Gunakan salah satu nilai STORAGE_PROTOCOL_NVME_DATA_TYPE berikut ini di bidang DataType dari struktur STORAGE_PROTOCOL_SPECIFIC_DATA:

    • Gunakan NVMeDataTypeIdentify untuk mendapatkan data Identifi Controller atau Identifikasi data Namespace.
    • Gunakan NVMeDataTypeLogPage untuk mendapatkan halaman log (termasuk data SMART/health).
    • Gunakan NVMeDataTypeFeature untuk mendapatkan fitur drive NVMe.
  • STORAGE_TEMPERATURE_INFO : Struktur ini digunakan untuk menyimpan data suhu tertentu. Ini digunakan dalam STORAGE_TEMERATURE_DATA_DESCRIPTOR untuk mengembalikan hasil kueri suhu.

  • IOCTL_STORAGE_SET_TEMPERATURE_THRESHOLD : Gunakan IOCTL ini dengan struktur STORAGE_TEMPERATURE_THRESHOLD untuk mengatur ambang suhu. Untuk informasi selengkapnya, lihat Perintah perubahan perilaku.

  • STORAGE_TEMPERATURE_THRESHOLD : Struktur ini digunakan sebagai buffer input untuk menentukan ambang suhu. Bidang OverThreshold (boolean) menentukan apakah bidang Ambang batas adalah nilai ambang batas atas atau tidak (jika tidak, bidang tersebut adalah nilai ambang batas bawah).

Mekanisme pass-through

Perintah yang tidak didefinisikan dalam spesifikasi NVMe adalah yang paling sulit untuk ditangani oleh OS host - host tidak memiliki wawasan tentang efek yang mungkin dimiliki perintah pada perangkat target, infrastruktur yang diekspos (namespace/ukuran blok), dan perilakunya.

Untuk membawa perintah khusus perangkat tersebut dengan lebih baik melalui tumpukan penyimpanan Windows, mekanisme pass-through baru memungkinkan perintah khusus vendor untuk disalurkan. Pipa pass-through ini juga akan membantu dalam pengembangan alat manajemen dan pengujian. Namun, mekanisme pass-through ini memerlukan penggunaan Log Efek Perintah. Selain itu, StoreNVMe.sys memerlukan semua perintah, bukan hanya perintah pass-through, untuk dijelaskan dalam Log Efek Perintah.

Penting

StorNVMe.sys dan Storport.sys akan memblokir perintah apa pun ke perangkat jika tidak dijelaskan dalam Log Efek Perintah.

 

Mendukung Log Efek Perintah

Log Efek Perintah (seperti yang dijelaskan dalam Perintah yang Didukung dan Efek, bagian 5.10.1.5 dari Spesifikasi NVMe 1.2) memungkinkan deskripsi efek perintah khusus vendor bersama dengan perintah yang ditentukan spesifikasi. Ini memfasilitasi validasi dukungan perintah serta pengoptimalan perilaku perintah, dan oleh karena itu harus diimplementasikan untuk seluruh set perintah yang didukung perangkat. Kondisi berikut menjelaskan hasil tentang bagaimana perintah dikirim berdasarkan entri Log Efek Perintahnya.

Untuk perintah tertentu yang dijelaskan dalam Log Efek Perintah...

Sementara:

  • Perintah Didukung (CSUPP) diatur ke '1' menandakan bahwa perintah didukung oleh pengontrol (Bit 01)

    Catatan

    Ketika CSUPP diatur ke '0' (menandakan bahwa perintah tidak didukung) perintah akan diblokir

     

Dan jika salah satu hal berikut ini diatur:

  • Controller Capability Change (CCC) diatur ke '1' menandakan bahwa perintah dapat mengubah kemampuan pengontrol (Bit 04)

  • Perubahan Persediaan Namespace (NIC) diatur ke '1' menandakan bahwa perintah dapat mengubah jumlah, atau kemampuan untuk beberapa namespace layanan (Bit 03)

  • Perubahan Kemampuan Namespace (NCC) diatur ke '1' menandakan bahwa perintah dapat mengubah kemampuan namespace layanan tunggal (Bit 02)

  • Pengiriman dan Eksekusi Perintah (CSE) diatur ke 001b atau 010b, menandakan bahwa perintah dapat dikirimkan ketika tidak ada perintah terutang lainnya ke namespace layanan yang sama atau apa pun, dan bahwa perintah lain tidak boleh dikirimkan ke namespace yang sama atau apa pun sampai perintah ini selesai (Bit 18:16)

Kemudian perintah akan dikirim sebagai satu-satunya perintah yang luar biasa ke adaptor.

Lain jika:

  • Pengiriman dan Eksekusi Perintah (CSE) diatur ke 001b, menandakan bahwa perintah dapat dikirimkan ketika tidak ada perintah terutang lainnya ke namespace yang sama, dan bahwa perintah lain tidak boleh dikirimkan ke namespace yang sama sampai perintah ini selesai (Bit 18:16)

Kemudian perintah akan dikirim sebagai satu-satunya perintah yang berutang ke objek Nomor Unit Logis (LUN).

Jika tidak, perintah dikirim dengan perintah lain yang luar biasa tanpa penghambatan. Misalnya, jika perintah khusus vendor dikirim ke perangkat untuk mengambil informasi statistik yang tidak ditentukan spesifikasi, seharusnya tidak ada risiko untuk mengubah perilaku atau kemampuan perangkat untuk menjalankan perintah I/O. Permintaan tersebut dapat dilayankan secara paralel dengan I/O dan tidak ada jeda-resume yang diperlukan.

Menggunakan IOCTL_STORAGE_PROTOCOL_COMMAND untuk mengirim perintah

Pass-through dapat dilakukan menggunakan IOCTL_STORAGE_PROTOCOL_COMMAND, diperkenalkan di Windows 10. IOCTL ini dirancang untuk memiliki perilaku yang sama dengan IOCTL pass-through SCSI dan ATA yang ada, untuk mengirim perintah yang disematkan ke perangkat target. Melalui IOCTL ini, pass-through dapat dikirim ke perangkat penyimpanan, termasuk drive NVMe.

Misalnya, di NVMe, IOCTL akan memungkinkan pengiriman kode perintah berikut.

  • Perintah Admin Khusus Vendor (C0h – FFh)
  • Perintah NVMe Spesifik Vendor (80h – FFh)

Seperti semua IOCTL lainnya, Gunakan DeviceIoControl untuk mengirim IOCTL pass-through ke bawah. IOCTL diisi menggunakan struktur STORAGE_PROTOCOL_COMMAND input-buffer yang ditemukan di ntddstor.h. Isi bidang Perintah dengan perintah khusus vendor.

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;

Perintah khusus vendor yang ingin dikirim harus diisi di bidang yang disorot di atas. Perhatikan lagi bahwa Log Efek Perintah harus diimplementasikan untuk perintah pass-through. Secara khusus, perintah ini perlu dilaporkan seperti yang didukung di Log Efek Perintah (lihat bagian sebelumnya untuk informasi selengkapnya). Perhatikan juga bahwa bidang PRP adalah driver khusus sehingga aplikasi yang mengirim perintah dapat meninggalkannya sebagai 0.

Terakhir, IOCTL pass-through ini ditujukan untuk mengirim perintah khusus vendor. Untuk mengirim perintah NVMe khusus admin atau non-vendor lainnya seperti Identifikasi, IOCTL pass-through ini tidak boleh digunakan. Misalnya, IOCTL_STORAGE_QUERY_PROPERTY harus digunakan untuk Mengidentifikasi atau Mendapatkan Halaman Log. Untuk informasi selengkapnya, lihat bagian berikutnya, Kueri khusus protokol.

Jangan memperbarui firmware melalui mekanisme pass-through

Perintah unduhan dan aktivasi firmware tidak boleh dikirim menggunakan pass-through. IOCTL_STORAGE_PROTOCOL_COMMAND hanya boleh digunakan untuk perintah khusus vendor.

Sebagai gantinya, gunakan IOCTL penyimpanan umum berikut (diperkenalkan di Windows 10) untuk menghindari aplikasi secara langsung menggunakan versi SCSI_miniport IOCTL Firmware. Driver penyimpanan akan menerjemahkan IOCTL ke perintah SCSI atau versi SCSI_miniport IOCTL ke miniport.

IOCTL ini direkomendasikan untuk mengembangkan alat peningkatan firmware di Windows 10 dan Windows Server 2016:

Untuk mendapatkan informasi penyimpanan dan memperbarui firmware, Windows juga mendukung cmdlet PowerShell untuk melakukan ini dengan cepat:

  • Get-StorageFirmwareInfo
  • Update-StorageFirmware

Catatan

Untuk memperbarui firmware pada NVMe di Windows 8.1, gunakan IOCTL_SCSI_MINIPORT_FIRMWARE. IOCTL ini tidak didukung ke Windows 7. Untuk informasi selengkapnya, lihat Meningkatkan Firmware untuk Perangkat NVMe di Windows 8.1.

 

Mengembalikan kesalahan melalui mekanisme pass-through

Mirip dengan IOCTL pass-through SCSI dan ATA, ketika perintah/permintaan dikirim ke miniport atau perangkat, IOCTL mengembalikan jika berhasil atau tidak. Dalam struktur STORAGE_PROTOCOL_COMMAND, IOCTL mengembalikan status melalui bidang ReturnStatus.

Contoh: mengirim perintah khusus vendor

Dalam contoh ini, perintah khusus vendor arbitrer (0xFF) dikirim melalui pass-through ke drive NVMe. Kode berikut mengalokasikan buffer, menginisialisasi kueri, lalu mengirim perintah ke perangkat melalui 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 
                             );  

Dalam contoh ini, kita mengharapkan protocolCommand->ReturnStatus == STORAGE_PROTOCOL_STATUS_SUCCESS apakah perintah berhasil ke perangkat.

Kueri khusus protokol

Windows 8.1 memperkenalkan IOCTL_STORAGE_QUERY_PROPERTY untuk pengambilan data. Di Windows 10, IOCTL ditingkatkan untuk mendukung fitur NVMe yang umum diminta seperti Dapatkan Halaman Log, Dapatkan Fitur, dan Identifikasi. Hal ini memungkinkan pengambilan informasi spesifik NVMe untuk tujuan pemantauan dan inventori.

Buffer input untuk IOCTL, STORAGE_PROPERTY_QUERY (dari Windows 10) ditampilkan di sini.

typedef struct _STORAGE_PROPERTY_QUERY {
    STORAGE_PROPERTY_ID PropertyId;
    STORAGE_QUERY_TYPE QueryType;
    UCHAR  AdditionalParameters[1];
} STORAGE_PROPERTY_QUERY, *PSTORAGE_PROPERTY_QUERY;

Saat menggunakan IOCTL_STORAGE_QUERY_PROPERTY untuk mengambil informasi khusus protokol NVMe di STORAGE_PROTOCOL_DATA_DESCRIPTOR, konfigurasikan struktur STORAGE_PROPERTY_QUERY sebagai berikut:

Struktur STORAGE_PROTOCOL_SPECIFIC_DATA (dari Windows 10) ditampilkan di sini.

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;

Untuk menentukan jenis informasi khusus protokol NVMe, konfigurasikan struktur STORAGE_PROTOCOL_SPECIFIC_DATA sebagai berikut:

  • Atur bidang ProtocolType ke ProtocolTypeNVMe.

  • Atur bidang DataType ke nilai enumerasi yang ditentukan oleh STORAGE_PROTOCOL_NVME_DATA_TYPE:

    • Gunakan NVMeDataTypeIdentify untuk mendapatkan data Identifi Controller atau Identifikasi data Namespace.
    • Gunakan NVMeDataTypeLogPage untuk mendapatkan halaman log (termasuk data SMART/health).
    • Gunakan NVMeDataTypeFeature untuk mendapatkan fitur drive NVMe.

Ketika ProtocolTypeNVMe digunakan sebagai ProtocolType, kueri untuk informasi khusus protokol dapat diambil secara paralel dengan I/O lain pada drive NVMe.

Penting

Untuk IOCTL_STORAGE_QUERY_PROPERTY yang menggunakan STORAGE_PROPERTY_ID StorageAdapterProtocolSpecificProperty, dan struktur STORAGE_PROTOCOL_SPECIFIC_DATA atau STORAGE_PROTOCOL_SPECIFIC_DATA_EXT nya diatur ke ProtocolType=ProtocolTypeNvme dan DataType=NVMeDataTypeLogPage, atur anggota ProtocolDataLength dari struktur yang sama ke nilai minimum 512 (byte).

Contoh berikut menunjukkan kueri khusus protokol NVMe.

Contoh: Kueri NVMe Identifi

Dalam contoh ini, permintaan Identifikasi dikirim ke drive NVMe. Kode berikut menginisialisasi struktur data kueri lalu mengirim perintah ke perangkat melalui 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"));
        }
    }

  

Penting

Untuk IOCTL_STORAGE_QUERY_PROPERTY yang menggunakan STORAGE_PROPERTY_ID StorageAdapterProtocolSpecificProperty, dan struktur STORAGE_PROTOCOL_SPECIFIC_DATA atau STORAGE_PROTOCOL_SPECIFIC_DATA_EXT nya diatur ke ProtocolType=ProtocolTypeNvme dan DataType=NVMeDataTypeLogPage, atur anggota ProtocolDataLength dari struktur yang sama ke nilai minimum 512 (byte).

Perhatikan bahwa pemanggil perlu mengalokasikan satu buffer yang berisi STORAGE_PROPERTY_QUERY dan ukuran STORAGE_PROTOCOL_SPECIFIC_DATA. Dalam contoh ini, ia menggunakan buffer yang sama untuk input dan output dari kueri properti. Itu sebabnya buffer yang dialokasikan memiliki ukuran "FIELD_OFFSET(STORAGE_PROPERTY_QUERY, AdditionalParameters) + sizeof(STORAGE_PROTOCOL_SPECIFIC_DATA) + NVME_MAX_LOG_SIZE". Meskipun buffer terpisah dapat dialokasikan untuk input dan output, sebaiknya gunakan satu buffer untuk mengkueri informasi terkait NVMe.

identifiControllerData-NN> adalah Jumlah Namespace (NN). Windows mendeteksi namespace sebagai drive fisik.

Contoh: Kueri NVMe Get Log Pages

Dalam contoh ini, berdasarkan yang sebelumnya, permintaan Dapatkan Halaman Log dikirim ke drive NVMe. Kode berikut menyiapkan struktur data kueri lalu mengirim perintah ke perangkat melalui 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"));
    }

Penelepon dapat menggunakan STORAGE_PROPERTY_ID StorageAdapterProtocolSpecificProperty, dan yang struktur STORAGE_PROTOCOL_SPECIFIC_DATA atau STORAGE_PROTOCOL_SPECIFIC_DATA_EXT nya diatur ke ProtocolDataRequestValue=VENDOR_SPECIFIC_LOG_PAGE_IDENTIFIER untuk meminta potongan 512 byte data spesifik vendor.

Contoh: Kueri NVMe Get Features

Dalam contoh ini, berdasarkan yang sebelumnya, permintaan Dapatkan Fitur dikirim ke drive NVMe. Kode berikut menyiapkan struktur data kueri lalu mengirim perintah ke perangkat melalui 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 khusus protokol

Dari Windows 10 19H1, IOCTL_STORAGE_SET_PROPERTY ditingkatkan untuk mendukung Fitur NVMe Set.

Buffer input untuk IOCTL_STORAGE_SET_PROPERTY ditampilkan di sini:

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;

Saat menggunakan IOCTL_STORAGE_SET_PROPERTY untuk mengatur fitur NVMe, konfigurasikan struktur STORAGE_PROPERTY_SET sebagai berikut:

  • Alokasikan buffer yang dapat berisi struktur STORAGE_PROPERTY_SET dan STORAGE_PROTOCOL_SPECIFIC_DATA_EXT;
  • Atur bidang PropertyID ke StorageAdapterProtocolSpecificProperty atau StorageDeviceProtocolSpecificProperty untuk permintaan pengontrol atau perangkat/namespace layanan.
  • Isi struktur STORAGE_PROTOCOL_SPECIFIC_DATA_EXT dengan nilai yang diinginkan. Awal STORAGE_PROTOCOL_SPECIFIC_DATA_EXT adalah bidang AdditionalParameters dari STORAGE_PROPERTY_SET.

Struktur STORAGE_PROTOCOL_SPECIFIC_DATA_EXT ditampilkan di sini.

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;

Untuk menentukan jenis fitur NVMe yang akan diatur, konfigurasikan struktur STORAGE_PROTOCOL_SPECIFIC_DATA_EXT sebagai berikut:

  • Atur bidang ProtocolType ke ProtocolTypeNvme;
  • Atur bidang DataType ke nilai enumerasi NVMeDataTypeFeature yang ditentukan oleh STORAGE_PROTOCOL_NVME_DATA_TYPE;

Contoh berikut menunjukkan set fitur NVMe.

Contoh: Fitur NVMe Set

Dalam contoh ini, permintaan Atur Fitur dikirim ke drive NVMe. Kode berikut menyiapkan struktur data yang ditetapkan lalu mengirim perintah ke perangkat melalui 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
            );

Kueri suhu

Di Windows 10, IOCTL_STORAGE_QUERY_PROPERTY juga dapat digunakan untuk mengkueri data suhu dari perangkat NVMe.

Untuk mengambil informasi suhu dari drive NVMe di STORAGE_TEMPERATURE_DATA_DESCRIPTOR, konfigurasikan struktur STORAGE_PROPERTY_QUERY sebagai berikut:

  • Alokasikan buffer yang dapat berisi struktur STORAGE_PROPERTY_QUERY.

  • Atur bidang PropertyID ke StorageAdapterTemperatureProperty atau StorageDeviceTemperatureProperty untuk pengontrol atau permintaan perangkat/namespace, masing-masing.

  • Atur bidang QueryType ke PropertyStandardQuery.

Struktur STORAGE_TEMPERATURE_INFO (dari Windows 10) ditampilkan di sini.

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;

Perintah perubahan perilaku

Perintah yang memanipulasi atribut perangkat atau berpotensi berdampak pada perilaku perangkat lebih sulit untuk ditangani sistem operasi. Jika atribut perangkat berubah pada run-time saat I/O sedang diproses, sinkronisasi atau masalah integritas data dapat muncul jika tidak ditangani dengan benar.

Perintah NVMe Set-Features adalah contoh yang baik dari perintah perubahan perilaku. Ini memungkinkan perubahan mekanisme arbitrase dan pengaturan ambang suhu. Untuk memastikan bahwa data dalam penerbangan tidak berisiko ketika perintah set yang memengaruhi perilaku dikirimkan, Windows akan menjeda semua I/O ke perangkat NVMe, menguras antrean, dan menghapus buffer. Setelah perintah set berhasil dijalankan, I/O dilanjutkan (jika memungkinkan). Jika I/O tidak dapat dilanjutkan, reset perangkat mungkin diperlukan.

Mengatur ambang batas suhu

Windows 10 memperkenalkan IOCTL_STORAGE_SET_TEMPERATURE_THRESHOLD, IOCTL untuk mendapatkan dan mengatur ambang suhu. Anda juga dapat menggunakannya untuk mendapatkan suhu perangkat saat ini. Buffer input/output untuk IOCTL ini adalah struktur STORAGE_TEMPERATURE_INFO , dari bagian kode sebelumnya.

Contoh: Mengatur suhu over-threshold

Dalam contoh ini, suhu over-threshold drive NVMe diatur. Kode berikut menyiapkan perintah lalu mengirimkannya ke perangkat melalui 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  
                             ); 

Mengatur fitur khusus vendor

Tanpa Log Efek Perintah, driver tidak memiliki pengetahuan tentang ramifikasi perintah. Inilah sebabnya mengapa Log Efek Perintah diperlukan. Ini membantu sistem operasi menentukan apakah perintah berdampak tinggi dan apakah dapat dikirim secara paralel dengan perintah lain ke drive.

Log Efek Perintah belum cukup terperinci untuk mencakup perintah Set-Features khusus vendor. Untuk alasan ini, belum dimungkinkan untuk mengirim perintah Set-Features khusus vendor. Namun, dimungkinkan untuk menggunakan mekanisme pass-through, yang dibahas sebelumnya, untuk mengirim perintah khusus vendor. Untuk informasi selengkapnya, lihat Mekanisme pass-through.

File Header

File berikut relevan dengan pengembangan NVMe. File-file ini disertakan dengan Microsoft Windows Software Development Kit (SDK).

File header Deskripsi
ntddstor.h Menentukan konstanta dan jenis untuk mengakses driver kelas penyimpanan dari mode kernel.
nvme.h Untuk struktur data terkait NVMe lainnya.
winioctl.h Untuk definisi Win32 IOCTL secara keseluruhan, termasuk API penyimpanan untuk aplikasi mode pengguna.