'공부/Windows'에 해당되는 글 3건

기존의 SATA SSD들은 WMI를 이용해서 매우 쉽게 스마트 정보를 얻어올 수 있었는데, NVMe 제품들은 이 방식으로 정보를 얻어올 수가 없었다.


현재는 MSDN에 (https://msdn.microsoft.com/en-us/library/windows/desktop/mt718131(v=vs.85).aspx)

NVMe에서 정보를 얻어오는 방법을 소개하고 있기때문에 참고하면 된다. 



요약하면 DeviceIoControl API와 IoControlCode (IOCTL_STORAGE_QUERY_PROPERTY)를 사용해서 얻어올 수 있다고 한다. ( Windows7의 경우는 핫픽스 업데이트 이후부터 가능, WIndows 8.1, Windows 10)


* nvme.h는 WDK를 설치하면 된다. winioctl.h 필요



핵심은 


STORAGE_PROPERTY_QUERY 구조체에다가 

PropertyId = StorageDeviceProtocolSpecificProperty

QueryType =  PropertyStandardQuery,

로 셋팅하고 


STORAGE_PROTOCOL_SPECIFIC_DATA 포인터를 만들어서 

STORAGE_PROPERTY_QUERY->AdditionalParameters 주소를 가리키게 만든다.


 

그 담 STORAGE_PROPERTY_QUERY 구조체의 값을 설정해준다.


ProtocolType = ProtocolTypeNvme; // 프로토콜 타입은 NVMe

DataType = NVMeDataTypeLogPage; // 데이터 타입은 LogPage

ProtocolDataRequestValue = NVME_LOG_PAGE_HEALTH_INFO; // SMART, Health Information은 02h

ProtocolDataRequestSubValue = 0;

ProtocolDataOffset = sizeof(STORAGE_PROTOCOL_SPECIFIC_DATA); // 40바이트

ProtocolDataLength = sizeof(NVME_HEALTH_INFO_LOG); // 512바이트


그 담 DeviceIoControl API를 호출해주면 output에 SMART 정보가 담겨나온다.

BOOL WINAPI DeviceIoControl(
  _In_        HANDLE       hDevice,
  _In_        DWORD        dwIoControlCode,
  _In_opt_    LPVOID       lpInBuffer,
  _In_        DWORD        nInBufferSize,
  _Out_opt_   LPVOID       lpOutBuffer,
  _In_        DWORD        nOutBufferSize,
  _Out_opt_   LPDWORD      lpBytesReturned,
  _Inout_opt_ LPOVERLAPPED lpOverlapped
);


NVME_HEALTH_INFO_LOG 구조체

//
// Information of log: NVME_LOG_PAGE_HEALTH_INFO. Size: 512 bytes
//
typedef struct {

    struct {
        UCHAR   AvailableSpaceLow   : 1;                    // If set to 1, then the available spare space has fallen below the threshold.
        UCHAR   TemperatureThreshold : 1;                   // If set to 1, then a temperature is above an over temperature threshold or below an under temperature threshold.
        UCHAR   ReliabilityDegraded : 1;                    // If set to 1, then the device reliability has been degraded due to significant media related  errors or any internal error that degrades device reliability.
        UCHAR   ReadOnly            : 1;                    // If set to 1, then the media has been placed in read only mode
        UCHAR   VolatileMemoryBackupDeviceFailed    : 1;    // If set to 1, then the volatile memory backup device has failed. This field is only valid if the controller has a volatile memory backup solution.
        UCHAR   Reserved                            : 3;
    } CriticalWarning;    // This field indicates critical warnings for the state of the  controller. Each bit corresponds to a critical warning type; multiple bits may be set.

    UCHAR   Temperature[2];                 // Temperature: Contains the temperature of the overall device (controller and NVM included) in units of Kelvin. If the temperature exceeds the temperature threshold, refer to section 5.12.1.4, then an asynchronous event completion may occur


	UCHAR   AvailableSpare;                 // Available Spare:  Contains a normalized percentage (0 to 100%) of the remaining spare capacity available
    UCHAR   AvailableSpareThreshold;        // Available Spare Threshold:  When the Available Spare falls below the threshold indicated in this field, an asynchronous event  completion may occur. The value is indicated as a normalized percentage (0 to 100%).
    UCHAR   PercentageUsed;                 // Percentage Used
    UCHAR   Reserved0[26];

	UCHAR  DataUnitRead[16];;               // Data Units Read:  Contains the number of 512 byte data units the host has read from the controller; this value does not include metadata. This value is reported in thousands (i.e., a value of 1 corresponds to 1000 units of 512 bytes read)  and is rounded up.  When the LBA size is a value other than 512 bytes, the controller shall convert the amount of data read to 512 byte units. For the NVM command set, logical blocks read as part of Compare and Read operations shall be included in this value
	UCHAR DataUnitWritten[16];            // Data Units Written: Contains the number of 512 byte data units the host has written to the controller; this value does not include metadata. This value is reported in thousands (i.e., a value of 1 corresponds to 1000 units of 512 bytes written)  and is rounded up.  When the LBA size is a value other than 512 bytes, the controller shall convert the amount of data written to 512 byte units. For the NVM command set, logical blocks written as part of Write operations shall be included in this value. Write Uncorrectable commands shall not impact this value.
	UCHAR HostReadCommands[16];           // Host Read Commands:  Contains the number of read commands  completed by  the controller. For the NVM command set, this is the number of Compare and Read commands. 
	UCHAR HostWrittenCommands[16];         // Host Write Commands:  Contains the number of write commands  completed by  the controller. For the NVM command set, this is the number of Write commands.
    UCHAR   ControllerBusyTime[16];         // Controller Busy Time:  Contains the amount of time the controller is busy with I/O commands. The controller is busy when there is a command outstanding to an I/O Queue (specifically, a command was issued via an I/O Submission Queue Tail doorbell write and the corresponding  completion queue entry  has not been posted yet to the associated I/O Completion Queue). This value is reported in minutes.
    UCHAR   PowerCycle[16];                 // Power Cycles: Contains the number of power cycles.
    UCHAR   PowerOnHours[16];               // Power On Hours: Contains the number of power-on hours. This does not include time that the controller was powered and in a low power state condition.
    UCHAR   UnsafeShutdowns[16];            // Unsafe Shutdowns: Contains the number of unsafe shutdowns. This count is incremented when a shutdown notification (CC.SHN) is not received prior to loss of power.
    UCHAR   MediaErrors[16];                // Media Errors:  Contains the number of occurrences where the controller detected an unrecovered data integrity error. Errors such as uncorrectable ECC, CRC checksum failure, or LBA tag mismatch are included in this field.
    UCHAR   ErrorInfoLogEntryCount[16];     // Number of Error Information Log Entries:  Contains the number of Error Information log entries over the life of the controller
    ULONG   WarningCompositeTemperatureTime;     // Warning Composite Temperature Time: Contains the amount of time in minutes that the controller is operational and the Composite Temperature is greater than or equal to the Warning Composite Temperature Threshold (WCTEMP) field and less than the Critical Composite Temperature Threshold (CCTEMP) field in the Identify Controller data structure
    ULONG   CriticalCompositeTemperatureTime;    // Critical Composite Temperature Time: Contains the amount of time in minutes that the controller is operational and the Composite Temperature is greater the Critical Composite Temperature Threshold (CCTEMP) field in the Identify Controller data structure
    USHORT  TemperatureSensor1;          // Contains the current temperature reported by temperature sensor 1.
    USHORT  TemperatureSensor2;          // Contains the current temperature reported by temperature sensor 2.
    USHORT  TemperatureSensor3;          // Contains the current temperature reported by temperature sensor 3.
    USHORT  TemperatureSensor4;          // Contains the current temperature reported by temperature sensor 4.
    USHORT  TemperatureSensor5;          // Contains the current temperature reported by temperature sensor 5.
    USHORT  TemperatureSensor6;          // Contains the current temperature reported by temperature sensor 6.
    USHORT  TemperatureSensor7;          // Contains the current temperature reported by temperature sensor 7.
    USHORT  TemperatureSensor8;          // Contains the current temperature reported by temperature sensor 8.
    UCHAR   Reserved1[296];
} NVME_HEALTH_INFO_LOG, *PNVME_HEALTH_INFO_LOG;



bool GetSMART::getSMARTattr(int idx, void* HEALTH_INFO) { HANDLE handle; STORAGE_PROPERTY_QUERY* spq; STORAGE_PROTOCOL_SPECIFIC_DATA* sppd; PSTORAGE_PROTOCOL_DATA_DESCRIPTOR ppdd; PNVME_HEALTH_INFO_LOG Health_Info; DWORD retbufSize; TCHAR path[100], sbuf2[100]; ULONG outputBufferLen = 4096; BYTE* outputBuffer; outputBuffer = new BYTE[outputBufferLen]; spq = new STORAGE_PROPERTY_QUERY[sizeof(STORAGE_PROPERTY_QUERY) + sizeof(STORAGE_PROTOCOL_SPECIFIC_DATA)]; ZeroMemory(outputBuffer, outputBufferLen); sppd = (PSTORAGE_PROTOCOL_SPECIFIC_DATA)spq->AdditionalParameters; sppd->ProtocolType = ProtocolTypeNvme; sppd->DataType = NVMeDataTypeLogPage; sppd->ProtocolDataRequestValue = NVME_LOG_PAGE_HEALTH_INFO; sppd->ProtocolDataRequestSubValue = 0; sppd->ProtocolDataOffset = sizeof(STORAGE_PROTOCOL_SPECIFIC_DATA); // 40바이트 sppd->ProtocolDataLength = sizeof(NVME_HEALTH_INFO_LOG); // 512바이트 sppd->FixedProtocolReturnData = 0; ZeroMemory((sppd->Reserved), sizeof(DWORD) * 3); spq->PropertyId = StorageDeviceProtocolSpecificProperty; spq->QueryType = PropertyStandardQuery; lstrcpyW(path, _T("\\\\.\\PHYSICALDRIVE")); wsprintf(sbuf2, _T("%d"), idx); lstrcat(path, sbuf2); handle = CreateFile(path, GENERIC_READ || GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, NULL, NULL); if (!handle) { wprintf_s(_T("핸들생성 실패")); return false; } else { if (!DeviceIoControl(handle, IOCTL_STORAGE_QUERY_PROPERTY, spq, sizeof(STORAGE_PROPERTY_QUERY) + sizeof(STORAGE_PROTOCOL_SPECIFIC_DATA), outputBuffer, outputBufferLen, &retbufSize, NULL)) { CloseHandle(handle); return false; } ppdd = (PSTORAGE_PROTOCOL_DATA_DESCRIPTOR)outputBuffer; Health_Info = (PNVME_HEALTH_INFO_LOG)((DWORD)outputBuffer + ppdd->Size); memcpy(HEALTH_INFO, Health_Info, sizeof(NVME_HEALTH_INFO_LOG)); CloseHandle(handle); } delete spq; delete outputBuffer; return true; }

사용은



int main()
{
	GetSMART gs;
	NVME_HEALTH_INFO_LOG Health_Info;
	
	if (gs.getSMARTattr(4,&Health_Info)) { // 4번은 PHYSICALDRIVE4
		/* Power Cycle -> */
		int* PCCount;
		PCCount = (int*)Health_Info.PowerCycle;
		printf("Power Cycle: %d\n", *PCCount);
		/* ---------------- */

		/* Temperature -> Kelvin
		Celsius= Kelvin-273.15  */
		unsigned short* Temperature;
		Temperature = (unsigned short*)Health_Info.Temperature;
		printf("Temperature: %dC\n", (*Temperature - 273));
		/* ---------------- */
	}
	
	
	//gs.test();
    return 0;
}
트림 활성화체크는..
bool GetSMART::TrimEnabled(int idx) {

	HANDLE handle;

	STORAGE_PROPERTY_QUERY spq;
	DEVICE_TRIM_DESCRIPTOR dtd;

	DWORD retbufSize;
	TCHAR path[100], sbuf2[100];
	bool retValue = false;
	spq.PropertyId = StorageDeviceTrimProperty;
	spq.QueryType = PropertyStandardQuery;
	
	//'-- TRIM
	lstrcpyW(path, _T("\\\\.\\PHYSICALDRIVE"));
	wsprintf(sbuf2, _T("%d"), idx);
	lstrcat(path, sbuf2);

	handle = CreateFile(path, GENERIC_READ || GENERIC_WRITE,
		FILE_SHARE_READ, NULL, OPEN_EXISTING, NULL, NULL);

	if (!handle) {
		wprintf_s(_T("핸들생성 실패"));
		return false;
	}
	else {
		DeviceIoControl(handle, IOCTL_STORAGE_QUERY_PROPERTY, &spq, sizeof(STORAGE_PROPERTY_QUERY), &dtd, sizeof(DEVICE_TRIM_DESCRIPTOR), &retbufSize, NULL);
		wprintf_s(_T("%d"), dtd.Version);
		if (dtd.TrimEnabled == 1) {
			retValue = true;
		}
		else {
			retValue = false;
		}
		CloseHandle(handle);
		return retValue;
	}

}

이런식


NVMe에 대한 더 자세한 세부정보는 http://www.nvmexpress.org/wp-content/uploads/NVM_Express_1_2_1_Gold_20160603.pdf 파일에서 참고하면 된다.




저작자 표시 비영리
신고
블로그 이미지

콩 lee하이

거대한 콩입니다.