Commit 3a5cf14d authored by Vladislav Vaintroub's avatar Vladislav Vaintroub

MDEV-24175 Windows - fix detection of SSD

parent 4e24b318
...@@ -7556,72 +7556,152 @@ os_file_set_umask(ulint umask) ...@@ -7556,72 +7556,152 @@ os_file_set_umask(ulint umask)
} }
#ifdef _WIN32 #ifdef _WIN32
static int win32_get_block_size(HANDLE volume_handle, const char *volume_name)
{
STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR disk_alignment;
STORAGE_PROPERTY_QUERY storage_query;
DWORD tmp;
memset(&storage_query, 0, sizeof(storage_query));
storage_query.PropertyId = StorageAccessAlignmentProperty;
storage_query.QueryType = PropertyStandardQuery;
if (os_win32_device_io_control(volume_handle,
IOCTL_STORAGE_QUERY_PROPERTY,
&storage_query,
sizeof storage_query,
&disk_alignment,
sizeof disk_alignment,
&tmp) && tmp == sizeof disk_alignment) {
return disk_alignment.BytesPerPhysicalSector;
}
switch (GetLastError()) { /* Checks whether physical drive is on SSD.*/
case ERROR_INVALID_FUNCTION: static bool is_drive_on_ssd(DWORD nr)
case ERROR_NOT_SUPPORTED: {
break; char physical_drive_path[32];
default: snprintf(physical_drive_path, sizeof(physical_drive_path),
os_file_handle_error_no_exit( "\\\\.\\PhysicalDrive%lu", nr);
volume_name,
"DeviceIoControl(IOCTL_STORAGE_QUERY_PROPERTY / StorageAccessAlignmentProperty)", HANDLE h= CreateFile(physical_drive_path, 0,
FALSE); FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
} nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
return 512; if (h == INVALID_HANDLE_VALUE)
return false;
DEVICE_SEEK_PENALTY_DESCRIPTOR seek_penalty;
STORAGE_PROPERTY_QUERY storage_query{};
storage_query.PropertyId= StorageDeviceSeekPenaltyProperty;
storage_query.QueryType= PropertyStandardQuery;
bool on_ssd= false;
DWORD bytes_written;
if (DeviceIoControl(h, IOCTL_STORAGE_QUERY_PROPERTY, &storage_query,
sizeof storage_query, &seek_penalty, sizeof seek_penalty,
&bytes_written, nullptr))
{
on_ssd= seek_penalty.IncursSeekPenalty;
}
else
{
on_ssd= false;
}
CloseHandle(h);
return on_ssd;
} }
static bool win32_is_ssd(HANDLE volume_handle) /*
Checks whether volume is on SSD, by checking all physical drives
in that volume.
*/
static bool is_volume_on_ssd(const char *volume_mount_point)
{ {
DWORD tmp; char volume_name[MAX_PATH];
DEVICE_SEEK_PENALTY_DESCRIPTOR seek_penalty;
STORAGE_PROPERTY_QUERY storage_query; if (!GetVolumeNameForVolumeMountPoint(volume_mount_point, volume_name,
memset(&storage_query, 0, sizeof(storage_query)); array_elements(volume_name)))
{
storage_query.PropertyId = StorageDeviceSeekPenaltyProperty; /* This can fail, e.g if file is on network share */
storage_query.QueryType = PropertyStandardQuery; return false;
if (os_win32_device_io_control(volume_handle,
IOCTL_STORAGE_QUERY_PROPERTY,
&storage_query,
sizeof storage_query,
&seek_penalty,
sizeof seek_penalty,
&tmp) && tmp == sizeof(seek_penalty)){
return !seek_penalty.IncursSeekPenalty;
} }
DEVICE_TRIM_DESCRIPTOR trim; /* Chomp last backslash, this is needed to open volume.*/
storage_query.PropertyId = StorageDeviceTrimProperty; size_t length= strlen(volume_name);
if (os_win32_device_io_control(volume_handle, if (length && volume_name[length - 1] == '\\')
IOCTL_STORAGE_QUERY_PROPERTY, volume_name[length - 1]= 0;
&storage_query,
sizeof storage_query, /* Open volume handle */
&trim, HANDLE volume_handle= CreateFile(
sizeof trim, volume_name, 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
&tmp) && tmp == sizeof trim) { nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
return trim.TrimEnabled;
if (volume_handle == INVALID_HANDLE_VALUE)
return false;
/*
Enumerate all volume extends, check whether all of them are on SSD
*/
/* Anticipate common case where there is only one extent.*/
VOLUME_DISK_EXTENTS single_extent;
/* But also have a place to manage allocated data.*/
std::unique_ptr<BYTE[]> lifetime;
DWORD bytes_written;
VOLUME_DISK_EXTENTS *extents= nullptr;
if (DeviceIoControl(volume_handle, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS,
nullptr, 0, &single_extent, sizeof(single_extent),
&bytes_written, nullptr))
{
/* Worked on the first try. Use the preallocated buffer.*/
extents= &single_extent;
}
else
{
VOLUME_DISK_EXTENTS *last_query= &single_extent;
while (GetLastError() == ERROR_MORE_DATA)
{
DWORD extentCount= last_query->NumberOfDiskExtents;
DWORD allocatedSize=
FIELD_OFFSET(VOLUME_DISK_EXTENTS, Extents[extentCount]);
lifetime.reset(new BYTE[allocatedSize]);
last_query= (VOLUME_DISK_EXTENTS *) lifetime.get();
if (DeviceIoControl(volume_handle, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS,
nullptr, 0, last_query, allocatedSize,
&bytes_written, nullptr))
{
extents= last_query;
break;
}
}
} }
return false; CloseHandle(volume_handle);
if (!extents)
return false;
for (DWORD i= 0; i < extents->NumberOfDiskExtents; i++)
if (!is_drive_on_ssd(extents->Extents[i].DiskNumber))
return false;
return true;
} }
#include <unordered_map>
static bool is_file_on_ssd(char *file_path)
{
/* Cache of volume_path => volume_info, protected by rwlock.*/
static std::unordered_map<std::string, bool> cache;
static SRWLOCK lock= SRWLOCK_INIT;
/* Preset result, in case something fails, e.g we're on network drive.*/
char volume_path[MAX_PATH];
if (!GetVolumePathName(file_path, volume_path, array_elements(volume_path)))
return false;
/* Try cached volume info first.*/
std::string volume_path_str(volume_path);
bool found;
bool result;
AcquireSRWLockShared(&lock);
auto e= cache.find(volume_path_str);
if ((found= e != cache.end()))
result= e->second;
ReleaseSRWLockShared(&lock);
if (found)
return result;
result= is_volume_on_ssd(volume_path);
/* Update cache */
AcquireSRWLockExclusive(&lock);
cache[volume_path_str]= result;
ReleaseSRWLockExclusive(&lock);
return result;
}
#endif #endif
/** Determine some file metadata when creating or reading the file. /** Determine some file metadata when creating or reading the file.
...@@ -7658,48 +7738,12 @@ void fil_node_t::find_metadata(os_file_t file ...@@ -7658,48 +7738,12 @@ void fil_node_t::find_metadata(os_file_t file
space->atomic_write_supported = space->purpose == FIL_TYPE_TEMPORARY space->atomic_write_supported = space->purpose == FIL_TYPE_TEMPORARY
|| space->purpose == FIL_TYPE_IMPORT; || space->purpose == FIL_TYPE_IMPORT;
#ifdef _WIN32 #ifdef _WIN32
block_size = 512; on_ssd = is_file_on_ssd(name);
on_ssd = false; FILE_STORAGE_INFO info;
// Open volume for this file, find out it "physical bytes per sector" if (GetFileInformationByHandleEx(
char volume[MAX_PATH + 4]; file, FileStorageInfo, &info, sizeof(info))) {
if (!GetVolumePathName(name, volume + 4, MAX_PATH)) { block_size = info.PhysicalBytesPerSectorForAtomicity;
os_file_handle_error_no_exit(name,
"GetVolumePathName()", FALSE);
return;
}
// Special prefix required for volume names.
memcpy(volume, "\\\\.\\", 4);
size_t len = strlen(volume);
if (volume[len - 1] == '\\') {
// Trim trailing backslash from volume name.
volume[len - 1] = 0;
}
HANDLE volume_handle = CreateFile(volume, FILE_READ_ATTRIBUTES,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
0, OPEN_EXISTING, 0, 0);
if (volume_handle != INVALID_HANDLE_VALUE) {
block_size = win32_get_block_size(volume_handle, volume);
on_ssd = win32_is_ssd(volume_handle);
CloseHandle(volume_handle);
} else { } else {
/*
Report error, unless it is expected, e.g
missing permissions, or error when trying to
open volume for UNC share.
*/
if (GetLastError() != ERROR_ACCESS_DENIED
&& GetDriveType(volume) == DRIVE_FIXED) {
os_file_handle_error_no_exit(volume, "CreateFile()", FALSE);
}
}
/* Currently we support file block size up to 4KiB */
if (block_size > 4096) {
block_size = 4096;
} else if (block_size < 512) {
block_size = 512; block_size = 512;
} }
#else #else
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment