Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Detecting SSD in Windows

Tags:

I would like to change the performance and behaviour of my C++ application, according to whether the system drive is an SSD or not. Example:

  • With SSD, I want my gameserver application to load each map fully, with all objects in order to maximize performance.
  • With HDD, I want my gameserver application to load only the essential objects and entities in each map, with no external objects loaded.

I've seen http://msdn.microsoft.com/en-gb/library/windows/desktop/aa364939(v=vs.85).aspx, which is a way of determining if a certain drive is a HDD, CD ROM, DVD ROM, Removable Media, etc, but it STILL can't detect whether the main system drive is an SSD. I've also seen Is there any way of detecting if a drive is a SSD?, but the solution only applies to Linux.

I thought that I could somehow generate a large fine (500MB), and then time how long it takes to write the file, but however other system variables can easily influence the result.

In Windows, using C++, is there any way to get whether the main system drive is an SSD or not?

like image 322
AStopher Avatar asked Apr 29 '14 11:04

AStopher


People also ask

Why is my PC not detecting my SSD?

The BIOS will not detect a SSD if the data cable is damaged or the connection is incorrect. Serial ATA cables, in particular, can sometimes fall out of their connection. Be sure to check your SATA cables are tightly connected to the SATA port connection.


2 Answers

Having done some research and using the info from the answers on this page, here's my implementation using C WinAPIs for Windows 7 and later:

//Open drive as such: "\\?\PhysicalDriveX" where X is the drive number //INFO: To get drive number from a logical drive letter, check this method: //      (But keep in mind that a single logical drive, or a volume, //       can span across several physical drives, as a "spanned volume.") //       http://stackoverflow.com/a/11683906/843732  #include <WinIoCtl.h> #include <Ntddscsi.h>  DWORD bytesReturned;  //As an example, let's test 1st physical drive HANDLE hDevice = ::CreateFile(L"\\\\?\\PhysicalDrive0",     GENERIC_READ | GENERIC_WRITE,       //We need write access to send ATA command to read RPMs     FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,     OPEN_EXISTING,  0, NULL); if(hDevice != INVALID_HANDLE_VALUE) {     //Check TRIM -- should be Y for SSD     _tprintf(L"TRIM=");      STORAGE_PROPERTY_QUERY spqTrim;     spqTrim.PropertyId = (STORAGE_PROPERTY_ID)StorageDeviceTrimProperty;     spqTrim.QueryType = PropertyStandardQuery;      bytesReturned = 0;     DEVICE_TRIM_DESCRIPTOR dtd = {0};     if(::DeviceIoControl(hDevice, IOCTL_STORAGE_QUERY_PROPERTY,         &spqTrim, sizeof(spqTrim), &dtd, sizeof(dtd), &bytesReturned, NULL) &&         bytesReturned == sizeof(dtd))     {         //Got it         _tprintf(L"%s", dtd.TrimEnabled ? L"Y" : L"N");     }     else     {         //Failed         int err = ::GetLastError();         _tprintf(L"?");     }       //Check the seek-penalty value -- should be N for SSD     _tprintf(L", seekPenalty=");      STORAGE_PROPERTY_QUERY spqSeekP;     spqSeekP.PropertyId = (STORAGE_PROPERTY_ID)StorageDeviceSeekPenaltyProperty;     spqSeekP.QueryType = PropertyStandardQuery;      bytesReturned = 0;     DEVICE_SEEK_PENALTY_DESCRIPTOR dspd = {0};     if(::DeviceIoControl(hDevice, IOCTL_STORAGE_QUERY_PROPERTY,         &spqSeekP, sizeof(spqSeekP), &dspd, sizeof(dspd), &bytesReturned, NULL) &&         bytesReturned == sizeof(dspd))     {         //Got it         _tprintf(L"%s", dspd.IncursSeekPenalty ? L"Y" : L"N");     }     else     {         //Failed         int err = ::GetLastError();         _tprintf(L"?");     }       //Get drive's RPMs reading -- should be 1 for SSD     //CODE SOURCE: https://emoacht.wordpress.com/2012/11/06/csharp-ssd/     _tprintf(L", RPM=");      ATAIdentifyDeviceQuery id_query;     memset(&id_query, 0, sizeof(id_query));      id_query.header.Length = sizeof(id_query.header);     id_query.header.AtaFlags = ATA_FLAGS_DATA_IN;     id_query.header.DataTransferLength = sizeof(id_query.data);     id_query.header.TimeOutValue = 5;   //Timeout in seconds     id_query.header.DataBufferOffset = offsetof(ATAIdentifyDeviceQuery, data[0]);     id_query.header.CurrentTaskFile[6] = 0xec; // ATA IDENTIFY DEVICE      bytesReturned = 0;     if(::DeviceIoControl(hDevice, IOCTL_ATA_PASS_THROUGH,         &id_query, sizeof(id_query), &id_query, sizeof(id_query), &bytesReturned, NULL) &&         bytesReturned == sizeof(id_query))     {         //Got it          //Index of nominal media rotation rate         //SOURCE: http://www.t13.org/documents/UploadedDocuments/docs2009/d2015r1a-ATAATAPI_Command_Set_-_2_ACS-2.pdf         //          7.18.7.81 Word 217         //QUOTE: Word 217 indicates the nominal media rotation rate of the device and is defined in table:         //          Value           Description         //          --------------------------------         //          0000h           Rate not reported         //          0001h           Non-rotating media (e.g., solid state device)         //          0002h-0400h     Reserved         //          0401h-FFFEh     Nominal media rotation rate in rotations per minute (rpm)         //                                  (e.g., 7 200 rpm = 1C20h)         //          FFFFh           Reserved         #define kNominalMediaRotRateWordIndex 217         _tprintf(L"%d", (UINT)id_query.data[kNominalMediaRotRateWordIndex]);     }     else     {         //Failed         int err = ::GetLastError();         _tprintf(L"?");     }       _tprintf(L"\n");     ::CloseHandle(hDevice); } 

In case you don't have driver DDK includes, here're some definitions:

#ifndef StorageDeviceTrimProperty #define StorageDeviceTrimProperty 8 #endif  #ifndef DEVICE_TRIM_DESCRIPTOR typedef struct _DEVICE_TRIM_DESCRIPTOR {   DWORD   Version;   DWORD   Size;   BOOLEAN TrimEnabled; } DEVICE_TRIM_DESCRIPTOR, *PDEVICE_TRIM_DESCRIPTOR; #endif   #ifndef StorageDeviceSeekPenaltyProperty #define StorageDeviceSeekPenaltyProperty 7 #endif  #ifndef DEVICE_SEEK_PENALTY_DESCRIPTOR typedef struct _DEVICE_SEEK_PENALTY_DESCRIPTOR {   DWORD   Version;   DWORD   Size;   BOOLEAN IncursSeekPenalty; } DEVICE_SEEK_PENALTY_DESCRIPTOR, *PDEVICE_SEEK_PENALTY_DESCRIPTOR; #endif   struct ATAIdentifyDeviceQuery {     ATA_PASS_THROUGH_EX header;     WORD data[256]; }; 

Lastly, conclusion of my tests.

I have several Samsung SSDs connected via a SATA cable, and one PCIe SSD drive that is connected directly to the logic board using PCIe slot. I also have one large internal Western Digital HDD (spinning drive) that is also connected via a SATA cable, and a couple of external spinning HDDs.

Here's what I get for them:

Samsung SSD 256GB:     TRIM=Y, seekPenalty=N, RPM=1 Samsung SSD 500GB:     TRIM=Y, seekPenalty=N, RPM=1 PCIs SSD:              TRIM=Y, seekPenalty=?, RPM=0 Internal WD HDD:       TRIM=N, seekPenalty=?, RPM=0 External WD HDD:       TRIM=?, seekPenalty=?, RPM=? External Cavalry HDD:  TRIM=?, seekPenalty=Y, RPM=? 

So as you see, in my case, the only parameter that is correct for all 6 drives is TRIM. I'm not saying that it will be in your case as well. It's just my finding with the drives that I own.

like image 108
c00000fd Avatar answered Sep 18 '22 18:09

c00000fd


I believe you are using the wrong tool. Instead of making assumptions based on a drive being an SSD you should make your code work well with slow and fast drives, for example by loading the essential objects first and the rest later. In three years the invention of [...] may make regular hard drives faster than SSDs which would break your code. Going purely based on speed will also work for RAM discs, NFS, USB3.0-sticks and other stuff you didn't or cannot thing about.

EDIT: A HDD is not actually the same as a slow SSD. While they are both fast at reading and writing a HDD needs significant time for seeking. It thus makes sense to use two different access strategies: picking the important data via random access for the SSD and sequentially reading for the HDD. You will probably get away with only implementing the sequential strategy as that should still work ok with SSDs. It makes more sense to check for a HDD instead of a SSD though, because you need to treat the HDD special while SSD, RAMdisc, NFS and so on should not suffer from seek times and can thus be treated the same.

like image 29
nwp Avatar answered Sep 20 '22 18:09

nwp