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:
- Cara mengirim perintah NVMe khusus vendor dengan pass-through
- Cara mengirim perintah Identifikasi, Dapatkan Fitur, atau Dapatkan Halaman Log ke drive NVMe
- Cara mendapatkan informasi suhu dari drive NVMe
- Cara melakukan perintah perubahan perilaku, seperti mengatur ambang batas suhu
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_QUERY dan 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:
Alokasikan buffer yang dapat berisi STORAGE_PROPERTY_QUERY dan struktur STORAGE_PROTOCOL_SPECIFIC_DATA.
Atur bidang PropertyID ke StorageAdapterProtocolSpecificProperty atau StorageDeviceProtocolSpecificProperty untuk permintaan pengontrol atau perangkat/namespace layanan.
Atur bidang QueryType ke PropertyStandardQuery.
Isi struktur STORAGE_PROTOCOL_SPECIFIC_DATA dengan nilai yang diinginkan. Awal STORAGE_PROTOCOL_SPECIFIC_DATA adalah bidang AdditionalParameters dari STORAGE_PROPERTY_QUERY.
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. |