summaryrefslogtreecommitdiff
path: root/storage
diff options
context:
space:
mode:
authorVladislav Vaintroub <wlad@mariadb.com>2020-11-09 19:47:44 +0100
committerVladislav Vaintroub <wlad@mariadb.com>2020-11-10 13:24:45 +0100
commit3a5cf14defd64003e403b677a6901a1d8a447703 (patch)
tree10eb919a7dcd242b930c887275e202b57341f61b /storage
parent4e24b3187cd5ffbbfce1890e1befe5095ab0fb1a (diff)
downloadmariadb-git-3a5cf14defd64003e403b677a6901a1d8a447703.tar.gz
MDEV-24175 Windows - fix detection of SSD
Diffstat (limited to 'storage')
-rw-r--r--storage/innobase/os/os0file.cc242
1 files changed, 143 insertions, 99 deletions
diff --git a/storage/innobase/os/os0file.cc b/storage/innobase/os/os0file.cc
index 6a1dce703cb..27b26d4f5b1 100644
--- a/storage/innobase/os/os0file.cc
+++ b/storage/innobase/os/os0file.cc
@@ -7556,72 +7556,152 @@ os_file_set_umask(ulint umask)
}
#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()) {
- case ERROR_INVALID_FUNCTION:
- case ERROR_NOT_SUPPORTED:
- break;
- default:
- os_file_handle_error_no_exit(
- volume_name,
- "DeviceIoControl(IOCTL_STORAGE_QUERY_PROPERTY / StorageAccessAlignmentProperty)",
- FALSE);
- }
- return 512;
+/* Checks whether physical drive is on SSD.*/
+static bool is_drive_on_ssd(DWORD nr)
+{
+ char physical_drive_path[32];
+ snprintf(physical_drive_path, sizeof(physical_drive_path),
+ "\\\\.\\PhysicalDrive%lu", nr);
+
+ HANDLE h= CreateFile(physical_drive_path, 0,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
+ 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;
- DEVICE_SEEK_PENALTY_DESCRIPTOR seek_penalty;
- STORAGE_PROPERTY_QUERY storage_query;
- memset(&storage_query, 0, sizeof(storage_query));
-
- storage_query.PropertyId = StorageDeviceSeekPenaltyProperty;
- storage_query.QueryType = PropertyStandardQuery;
-
- 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;
+ char volume_name[MAX_PATH];
+
+ if (!GetVolumeNameForVolumeMountPoint(volume_mount_point, volume_name,
+ array_elements(volume_name)))
+ {
+ /* This can fail, e.g if file is on network share */
+ return false;
}
- DEVICE_TRIM_DESCRIPTOR trim;
- storage_query.PropertyId = StorageDeviceTrimProperty;
- if (os_win32_device_io_control(volume_handle,
- IOCTL_STORAGE_QUERY_PROPERTY,
- &storage_query,
- sizeof storage_query,
- &trim,
- sizeof trim,
- &tmp) && tmp == sizeof trim) {
- return trim.TrimEnabled;
+ /* Chomp last backslash, this is needed to open volume.*/
+ size_t length= strlen(volume_name);
+ if (length && volume_name[length - 1] == '\\')
+ volume_name[length - 1]= 0;
+
+ /* Open volume handle */
+ HANDLE volume_handle= CreateFile(
+ volume_name, 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
+
+ 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
/** Determine some file metadata when creating or reading the 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->purpose == FIL_TYPE_IMPORT;
#ifdef _WIN32
- block_size = 512;
- on_ssd = false;
- // Open volume for this file, find out it "physical bytes per sector"
- char volume[MAX_PATH + 4];
- if (!GetVolumePathName(name, volume + 4, MAX_PATH)) {
- 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);
+ on_ssd = is_file_on_ssd(name);
+ FILE_STORAGE_INFO info;
+ if (GetFileInformationByHandleEx(
+ file, FileStorageInfo, &info, sizeof(info))) {
+ block_size = info.PhysicalBytesPerSectorForAtomicity;
} 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;
}
#else