Read storage performance sensors without WMI
This commit is contained in:
@@ -442,6 +442,7 @@ internal sealed class AmdGpu : GenericGpu
|
||||
/// <param name="sensorType">Type of the sensor.</param>
|
||||
/// <param name="sensor">The sensor.</param>
|
||||
/// <param name="factor">The factor.</param>
|
||||
/// <param name="reset">If set to <c>true</c>, resets the sensor value to <c>null</c>.</param>
|
||||
private void GetPMLog(AtiAdlxx.ADLPMLogDataOutput data, AtiAdlxx.ADLSensorType sensorType, Sensor sensor, float factor = 1.0f, bool reset = true)
|
||||
{
|
||||
int i = (int)sensorType;
|
||||
|
||||
@@ -5,10 +5,10 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Management;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using LibreHardwareMonitor.Interop;
|
||||
|
||||
@@ -21,16 +21,15 @@ public abstract class AbstractStorage : Hardware
|
||||
private readonly StorageInfo _storageInfo;
|
||||
private readonly TimeSpan _updateInterval = TimeSpan.FromSeconds(60);
|
||||
|
||||
private ulong _lastReadRateCounter;
|
||||
private double _lastTime;
|
||||
private ulong _lastReadCount;
|
||||
private long _lastTime;
|
||||
private DateTime _lastUpdate = DateTime.MinValue;
|
||||
private ulong _lastWriteRateCounter;
|
||||
private ulong _lastWriteCount;
|
||||
private Sensor _sensorDiskReadRate;
|
||||
private Sensor _sensorDiskTotalActivity;
|
||||
private Sensor _sensorDiskWriteActivity;
|
||||
private Sensor _sensorDiskWriteRate;
|
||||
private Sensor _usageSensor;
|
||||
private int _wmiFailureCount;
|
||||
|
||||
internal AbstractStorage(StorageInfo storageInfo, string name, string firmwareRevision, string id, int index, ISettings settings)
|
||||
: base(name, new Identifier(id, index.ToString(CultureInfo.InvariantCulture)), settings)
|
||||
@@ -69,19 +68,24 @@ public abstract class AbstractStorage : Hardware
|
||||
|
||||
public int Index { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Close()
|
||||
{
|
||||
_storageInfo.Handle?.Close();
|
||||
base.Close();
|
||||
}
|
||||
|
||||
public static AbstractStorage CreateInstance(string deviceId, uint driveNumber, ulong diskSize, int scsiPort, ISettings settings)
|
||||
{
|
||||
StorageInfo info = WindowsStorage.GetStorageInfo(deviceId, driveNumber);
|
||||
if (info == null)
|
||||
if (info == null || info.Removable || info.BusType is Kernel32.STORAGE_BUS_TYPE.BusTypeVirtual or Kernel32.STORAGE_BUS_TYPE.BusTypeFileBackedVirtual)
|
||||
return null;
|
||||
|
||||
info.DiskSize = diskSize;
|
||||
info.DeviceId = deviceId;
|
||||
info.Handle = Kernel32.OpenDevice(deviceId);
|
||||
info.Scsi = $@"\\.\SCSI{scsiPort}:";
|
||||
|
||||
if (info.Removable || info.BusType is Kernel32.STORAGE_BUS_TYPE.BusTypeVirtual or Kernel32.STORAGE_BUS_TYPE.BusTypeFileBackedVirtual)
|
||||
return null;
|
||||
|
||||
|
||||
//fallback, when it is not possible to read out with the nvme implementation,
|
||||
//try it with the sata smart implementation
|
||||
if (info.BusType == Kernel32.STORAGE_BUS_TYPE.BusTypeNvme)
|
||||
@@ -119,78 +123,22 @@ public abstract class AbstractStorage : Hardware
|
||||
|
||||
protected abstract void UpdateSensors();
|
||||
|
||||
private void UpdateStatisticsFromWmi(int driveIndex)
|
||||
{
|
||||
string query = $"SELECT * FROM Win32_PerfRawData_PerfDisk_PhysicalDisk Where Name LIKE \"{driveIndex}%\"";
|
||||
|
||||
using var perfData = new ManagementObjectSearcher(query) { Options = { Timeout = TimeSpan.FromSeconds(7.5) } };
|
||||
using ManagementObjectCollection collection = perfData.Get();
|
||||
using ManagementObject data = collection.OfType<ManagementObject>().FirstOrDefault();
|
||||
|
||||
if (data == null)
|
||||
return;
|
||||
|
||||
ulong value = (ulong)data.Properties["PercentDiskWriteTime"].Value;
|
||||
ulong valueBase = (ulong)data.Properties["PercentDiskWriteTime_Base"].Value;
|
||||
_perfWrite.Update(value, valueBase);
|
||||
_sensorDiskWriteActivity.Value = (float)_perfWrite.Result;
|
||||
|
||||
value = (ulong)data.Properties["PercentIdleTime"].Value;
|
||||
valueBase = (ulong)data.Properties["PercentIdleTime_Base"].Value;
|
||||
_perfTotal.Update(value, valueBase);
|
||||
_sensorDiskTotalActivity.Value = (float)(100.0 - _perfTotal.Result);
|
||||
|
||||
ulong readRateCounter = (ulong)data.Properties["DiskReadBytesPerSec"].Value;
|
||||
ulong readRate = readRateCounter - _lastReadRateCounter;
|
||||
_lastReadRateCounter = readRateCounter;
|
||||
|
||||
ulong writeRateCounter = (ulong)data.Properties["DiskWriteBytesPerSec"].Value;
|
||||
ulong writeRate = writeRateCounter - _lastWriteRateCounter;
|
||||
_lastWriteRateCounter = writeRateCounter;
|
||||
|
||||
ulong timestampPerfTime = (ulong)data.Properties["Timestamp_PerfTime"].Value;
|
||||
ulong frequencyPerfTime = (ulong)data.Properties["Frequency_Perftime"].Value;
|
||||
double currentTime = (double)timestampPerfTime / frequencyPerfTime;
|
||||
|
||||
double timeDeltaSeconds = currentTime - _lastTime;
|
||||
if (_lastTime == 0 || timeDeltaSeconds > 0.2)
|
||||
{
|
||||
double writeSpeed = writeRate * (1 / timeDeltaSeconds);
|
||||
_sensorDiskWriteRate.Value = (float)writeSpeed;
|
||||
|
||||
double readSpeed = readRate * (1 / timeDeltaSeconds);
|
||||
_sensorDiskReadRate.Value = (float)readSpeed;
|
||||
}
|
||||
|
||||
if (_lastTime == 0 || timeDeltaSeconds > 0.2)
|
||||
_lastTime = currentTime;
|
||||
}
|
||||
|
||||
public override void Update()
|
||||
{
|
||||
const int wmiRetries = 10;
|
||||
|
||||
//update statistics from WMI on every update
|
||||
if (_storageInfo != null && _wmiFailureCount <= wmiRetries)
|
||||
// Update statistics.
|
||||
if (_storageInfo != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
UpdateStatisticsFromWmi(_storageInfo.Index);
|
||||
_wmiFailureCount = 0;
|
||||
UpdatePerformanceSensors();
|
||||
}
|
||||
catch
|
||||
{
|
||||
if (++_wmiFailureCount == wmiRetries)
|
||||
{
|
||||
DeactivateSensor(_sensorDiskTotalActivity);
|
||||
DeactivateSensor(_sensorDiskWriteActivity);
|
||||
DeactivateSensor(_sensorDiskReadRate);
|
||||
DeactivateSensor(_sensorDiskWriteRate);
|
||||
}
|
||||
// Ignored.
|
||||
}
|
||||
}
|
||||
|
||||
//read out with updateInterval
|
||||
// Read out at update interval.
|
||||
TimeSpan tDiff = DateTime.UtcNow - _lastUpdate;
|
||||
if (tDiff > _updateInterval)
|
||||
{
|
||||
@@ -220,13 +168,56 @@ public abstract class AbstractStorage : Hardware
|
||||
}
|
||||
|
||||
if (totalSize > 0)
|
||||
_usageSensor.Value = 100.0f - ((100.0f * totalFreeSpace) / totalSize);
|
||||
_usageSensor.Value = 100.0f - (100.0f * totalFreeSpace / totalSize);
|
||||
else
|
||||
_usageSensor.Value = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdatePerformanceSensors()
|
||||
{
|
||||
if (!Kernel32.DeviceIoControl(_storageInfo.Handle,
|
||||
Kernel32.IOCTL.IOCTL_DISK_PERFORMANCE,
|
||||
IntPtr.Zero,
|
||||
0,
|
||||
out Kernel32.DISK_PERFORMANCE diskPerformance,
|
||||
Marshal.SizeOf<Kernel32.DISK_PERFORMANCE>(),
|
||||
out _,
|
||||
IntPtr.Zero))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_perfWrite.Update(diskPerformance.WriteTime, diskPerformance.QueryTime);
|
||||
_sensorDiskWriteActivity.Value = (float)_perfWrite.Result;
|
||||
|
||||
_perfTotal.Update(diskPerformance.IdleTime, diskPerformance.QueryTime);
|
||||
_sensorDiskTotalActivity.Value = (float)(100 - _perfTotal.Result);
|
||||
|
||||
ulong readCount = diskPerformance.BytesRead;
|
||||
ulong readDiff = readCount - _lastReadCount;
|
||||
_lastReadCount = readCount;
|
||||
|
||||
ulong writeCount = diskPerformance.BytesWritten;
|
||||
ulong writeDiff = writeCount - _lastWriteCount;
|
||||
_lastWriteCount = writeCount;
|
||||
|
||||
long currentTime = Stopwatch.GetTimestamp();
|
||||
if (_lastTime != 0)
|
||||
{
|
||||
double timeDeltaSeconds = TimeSpan.FromTicks(currentTime - _lastTime).TotalSeconds;
|
||||
|
||||
double writeSpeed = writeDiff * (1 / timeDeltaSeconds);
|
||||
_sensorDiskWriteRate.Value = (float)writeSpeed;
|
||||
|
||||
double readSpeed = readDiff * (1 / timeDeltaSeconds);
|
||||
_sensorDiskReadRate.Value = (float)readSpeed;
|
||||
}
|
||||
|
||||
_lastTime = currentTime;
|
||||
}
|
||||
|
||||
protected abstract void GetReport(StringBuilder r);
|
||||
|
||||
public override string GetReport()
|
||||
@@ -294,4 +285,4 @@ public abstract class AbstractStorage : Hardware
|
||||
Result = 100;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using LibreHardwareMonitor.Interop;
|
||||
using Microsoft.Win32.SafeHandles;
|
||||
|
||||
namespace LibreHardwareMonitor.Hardware.Storage;
|
||||
|
||||
@@ -117,7 +118,7 @@ internal class NVMeIntel : INVMeDrive
|
||||
|
||||
public static SafeHandle IdentifyDevice(StorageInfo storageInfo)
|
||||
{
|
||||
SafeHandle handle = Kernel32.OpenDevice(storageInfo.Scsi);
|
||||
SafeFileHandle handle = Kernel32.OpenDevice(storageInfo.Scsi);
|
||||
if (handle?.IsInvalid != false)
|
||||
return null;
|
||||
|
||||
@@ -152,4 +153,4 @@ internal class NVMeIntel : INVMeDrive
|
||||
}
|
||||
return handle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using LibreHardwareMonitor.Interop;
|
||||
using Microsoft.Win32.SafeHandles;
|
||||
|
||||
namespace LibreHardwareMonitor.Hardware.Storage;
|
||||
|
||||
@@ -118,7 +119,7 @@ internal class NVMeIntelRst : INVMeDrive
|
||||
|
||||
public static SafeHandle IdentifyDevice(StorageInfo storageInfo)
|
||||
{
|
||||
SafeHandle handle = Kernel32.OpenDevice(storageInfo.Scsi);
|
||||
SafeFileHandle handle = Kernel32.OpenDevice(storageInfo.Scsi);
|
||||
if (handle?.IsInvalid != false)
|
||||
return null;
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ using System;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using LibreHardwareMonitor.Interop;
|
||||
using Microsoft.Win32.SafeHandles;
|
||||
|
||||
namespace LibreHardwareMonitor.Hardware.Storage;
|
||||
|
||||
@@ -179,7 +180,7 @@ internal class NVMeSamsung : INVMeDrive
|
||||
|
||||
public static SafeHandle IdentifyDevice(StorageInfo storageInfo)
|
||||
{
|
||||
SafeHandle handle = Kernel32.OpenDevice(storageInfo.DeviceId);
|
||||
SafeFileHandle handle = Kernel32.OpenDevice(storageInfo.DeviceId);
|
||||
if (handle?.IsInvalid != false)
|
||||
return null;
|
||||
|
||||
@@ -256,4 +257,4 @@ internal class NVMeSamsung : INVMeDrive
|
||||
|
||||
return handle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using LibreHardwareMonitor.Interop;
|
||||
using Microsoft.Win32.SafeHandles;
|
||||
|
||||
namespace LibreHardwareMonitor.Hardware.Storage;
|
||||
|
||||
@@ -94,7 +95,7 @@ internal class NVMeWindows : INVMeDrive
|
||||
|
||||
public static SafeHandle IdentifyDevice(StorageInfo storageInfo)
|
||||
{
|
||||
SafeHandle handle = Kernel32.OpenDevice(storageInfo.DeviceId);
|
||||
SafeFileHandle handle = Kernel32.OpenDevice(storageInfo.DeviceId);
|
||||
if (handle?.IsInvalid != false)
|
||||
return null;
|
||||
|
||||
@@ -124,4 +125,4 @@ internal class NVMeWindows : INVMeDrive
|
||||
|
||||
return handle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
// All Rights Reserved.
|
||||
|
||||
using LibreHardwareMonitor.Interop;
|
||||
using Microsoft.Win32.SafeHandles;
|
||||
|
||||
namespace LibreHardwareMonitor.Hardware.Storage;
|
||||
|
||||
@@ -16,6 +17,8 @@ internal abstract class StorageInfo
|
||||
|
||||
public ulong DiskSize { get; set; }
|
||||
|
||||
public SafeFileHandle Handle { get; set; }
|
||||
|
||||
public int Index { get; protected set; }
|
||||
|
||||
public string Name => (Vendor + " " + Product).Trim();
|
||||
@@ -33,4 +36,4 @@ internal abstract class StorageInfo
|
||||
public string Serial { get; protected set; }
|
||||
|
||||
public string Vendor { get; protected set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ using System.Collections.Generic;
|
||||
using System.Management;
|
||||
using System.Runtime.InteropServices;
|
||||
using LibreHardwareMonitor.Interop;
|
||||
using Microsoft.Win32.SafeHandles;
|
||||
|
||||
namespace LibreHardwareMonitor.Hardware.Storage;
|
||||
|
||||
@@ -15,7 +16,7 @@ internal static class WindowsStorage
|
||||
{
|
||||
public static Storage.StorageInfo GetStorageInfo(string deviceId, uint driveIndex)
|
||||
{
|
||||
using SafeHandle handle = Kernel32.OpenDevice(deviceId);
|
||||
using SafeFileHandle handle = Kernel32.OpenDevice(deviceId);
|
||||
|
||||
if (handle?.IsInvalid != false)
|
||||
return null;
|
||||
|
||||
@@ -80,9 +80,9 @@ public class Kernel32
|
||||
return result;
|
||||
}
|
||||
|
||||
internal static SafeHandle OpenDevice(string devicePath)
|
||||
internal static SafeFileHandle OpenDevice(string devicePath)
|
||||
{
|
||||
SafeHandle hDevice = CreateFile(devicePath, FileAccess.ReadWrite, FileShare.ReadWrite, IntPtr.Zero, FileMode.Open, FileAttributes.Normal, IntPtr.Zero);
|
||||
SafeFileHandle hDevice = CreateFile(devicePath, FileAccess.ReadWrite, FileShare.ReadWrite, IntPtr.Zero, FileMode.Open, FileAttributes.Normal, IntPtr.Zero);
|
||||
if (hDevice.IsInvalid || hDevice.IsClosed)
|
||||
hDevice = null;
|
||||
|
||||
@@ -218,6 +218,20 @@ public class Kernel32
|
||||
out uint lpBytesReturned,
|
||||
IntPtr lpOverlapped);
|
||||
|
||||
[DllImport(DllName, CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Auto, SetLastError = true)]
|
||||
[DefaultDllImportSearchPaths(DllImportSearchPath.System32)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static extern bool DeviceIoControl
|
||||
(
|
||||
SafeHandle hDevice,
|
||||
IOCTL dwIoControlCode,
|
||||
IntPtr lpInBuffer,
|
||||
int nInBufferSize,
|
||||
out DISK_PERFORMANCE lpOutBuffer,
|
||||
int nOutBufferSize,
|
||||
out uint lpBytesReturned,
|
||||
IntPtr lpOverlapped);
|
||||
|
||||
[DllImport(DllName, CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Auto, SetLastError = true)]
|
||||
[DefaultDllImportSearchPaths(DllImportSearchPath.System32)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
@@ -395,6 +409,7 @@ public class Kernel32
|
||||
IOCTL_SCSI_MINIPORT = 0x04d008,
|
||||
IOCTL_SCSI_PASS_THROUGH_DIRECT = 0x04d014,
|
||||
IOCTL_SCSI_GET_ADDRESS = 0x41018,
|
||||
IOCTL_DISK_PERFORMANCE = 0x70020,
|
||||
IOCTL_STORAGE_QUERY_PROPERTY = 0x2D1400,
|
||||
IOCTL_BATTERY_QUERY_TAG = 0x294040,
|
||||
IOCTL_BATTERY_QUERY_INFORMATION = 0x294044,
|
||||
@@ -881,6 +896,49 @@ public class Kernel32
|
||||
public STORAGE_BUS_TYPE BusType;
|
||||
public uint RawPropertiesLength;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
||||
public struct DISK_PERFORMANCE
|
||||
{
|
||||
/// <nodoc />
|
||||
public ulong BytesRead;
|
||||
|
||||
/// <nodoc />
|
||||
public ulong BytesWritten;
|
||||
|
||||
/// <nodoc />
|
||||
public ulong ReadTime;
|
||||
|
||||
/// <nodoc />
|
||||
public ulong WriteTime;
|
||||
|
||||
/// <nodoc />
|
||||
public ulong IdleTime;
|
||||
|
||||
/// <nodoc />
|
||||
public uint ReadCount;
|
||||
|
||||
/// <nodoc />
|
||||
public uint WriteCount;
|
||||
|
||||
/// <nodoc />
|
||||
public uint QueueDepth;
|
||||
|
||||
/// <nodoc />
|
||||
public uint SplitCount;
|
||||
|
||||
/// <nodoc />
|
||||
public ulong QueryTime;
|
||||
|
||||
/// <nodoc />
|
||||
public int StorageDeviceNumber;
|
||||
|
||||
/// <nodoc />
|
||||
public long StorageManagerName0;
|
||||
|
||||
/// <nodoc />
|
||||
public long StorageManagerName1;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct SRB_IO_CONTROL
|
||||
|
||||
Reference in New Issue
Block a user