Skip to content

fix: false hdd detection on virtual devices#352

Draft
KSXGitHub wants to merge 3 commits intoKSXGitHub:masterfrom
khai-slop-labs:claude/fix-hdd-detection-OQvIf
Draft

fix: false hdd detection on virtual devices#352
KSXGitHub wants to merge 3 commits intoKSXGitHub:masterfrom
khai-slop-labs:claude/fix-hdd-detection-OQvIf

Conversation

@KSXGitHub
Copy link
Owner

Summary

This PR fixes an issue where virtual block devices (VirtIO, Xen, Hyper-V, VMware) on Linux systems are incorrectly reported as HDDs instead of Unknown disks. The kernel's rotational sysfs flag defaults to 1 for virtual devices when the backing storage type cannot be determined, causing sysinfo to misclassify them.

Key Changes

  • Added correct_hdd_detection() function: Intercepts HDD classification on Linux to verify if the disk is actually backed by a virtual driver
  • Added extract_block_device_name() function: Parses device paths (e.g., /dev/vda1vda) and handles symlinks like /dev/mapper/ and /dev/root
  • Added is_virtual_block_device() function: Checks the driver symlink at /sys/block/<dev>/device/driver to identify virtual drivers (virtio_blk, xen_blkfront, vmw_pvscsi, hyperv_storvsc)
  • Removed #[inline] attribute from get_disk_kind() to accommodate the new logic
  • Added comprehensive Linux-specific tests: Unit tests for block device name extraction and virtual device detection, plus an integration test using real system disks

Implementation Details

  • The solution is Linux-specific; non-Linux platforms simply return DiskKind::HDD as before
  • Virtual devices are reclassified as DiskKind::Unknown(-1) instead of HDD
  • The implementation safely handles various device naming schemes (sd*, vd*, xvd*, nvme*, mmcblk*) and validates block device existence in sysfs before processing
  • Tests include both unit tests with hardcoded paths and integration tests that verify behavior on the actual system

https://claude.ai/code/session_01MHKaZm6DcA9kjBPnW3KN5K

The sysinfo crate reads /sys/block/*/queue/rotational to determine disk
type, but virtual block devices (VirtIO, Xen, VMware PVSCSI, Hyper-V)
default to rotational=1 because the kernel doesn't know the backing
storage type. This caused pdu to falsely detect virtual disks as HDDs
and unnecessarily limit threads to 1.

The fix adds a secondary check on Linux: when sysinfo reports HDD, we
read the block device's driver via /sys/block/<dev>/device/driver and
reclassify known virtual drivers (virtio_blk, xen_blkfront, vmw_pvscsi,
hyperv_storvsc) as Unknown instead of HDD.

https://claude.ai/code/session_01MHKaZm6DcA9kjBPnW3KN5K
Apply rustfmt formatting and replace map_or with is_some_and per clippy.

https://claude.ai/code/session_01MHKaZm6DcA9kjBPnW3KN5K
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adjusts HDD detection on Linux to avoid misclassifying virtual block devices as HDDs when sysinfo reports DiskKind::HDD due to the kernel’s rotational=1 default on some virtual devices.

Changes:

  • Intercept DiskKind::HDD on Linux and reclassify known virtual block-device drivers to DiskKind::Unknown(-1).
  • Add Linux-only helpers to extract a base block device name and check the sysfs driver symlink.
  • Add Linux-specific tests for device-name extraction and virtual-driver detection.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 5 comments.

File Description
src/app/hdd.rs Adds Linux-only correction path for HDD detection via /sys/block/<dev>/device/driver, plus device-name extraction.
src/app/hdd/test.rs Adds Linux-only tests around extraction and virtual device detection (currently environment-dependent).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +130 to +133
};

let driver_name = target
.file_name()
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is_virtual_block_device matches the Hyper-V storage driver name as hyperv_storvsc, but the in-kernel driver symlink name is typically hv_storvsc/storvsc. As written, Hyper-V virtual disks are likely to remain misclassified. Consider matching the actual driver names (and/or checking for a substring/prefix) so this works on Hyper-V hosts.

Copilot uses AI. Check for mistakes.
src/app/hdd.rs Outdated
Comment on lines +63 to +76
/// Extract the base block device name from a device path.
///
/// For example:
/// - `/dev/vda1` → `vda`
/// - `/dev/sda1` → `sda`
/// - `/dev/xvda1` → `xvda`
/// - `/dev/nvme0n1p1` → `nvme0n1`
/// - `/dev/mapper/xxx` → follows symlink, then recurses
#[cfg(target_os = "linux")]
fn extract_block_device_name(device_path: &str) -> Option<String> {
use std::fs;

if device_path.starts_with("/dev/mapper/") || device_path.starts_with("/dev/root") {
let real = fs::canonicalize(device_path).ok()?;
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

extract_block_device_name handles /dev/root in code, but the doc comment only mentions /dev/mapper/xxx symlinks. Please update the documentation/examples to include /dev/root (and clarify what happens when canonicalization resolves to dm-*).

Copilot uses AI. Check for mistakes.
src/app/hdd.rs Outdated
Comment on lines +107 to +113
// Verify this block device exists in sysfs
let sysfs_path = Path::new("/sys/block").join(block_dev);
if sysfs_path.exists() {
Some(block_dev.to_string())
} else {
None
}
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

extract_block_device_name mixes parsing with an environment-dependent /sys/block/<dev> existence check. This makes the function return None for perfectly valid device strings on systems where sysfs is unavailable/restricted (and makes the unit tests mostly conditional/no-op). Consider splitting into (1) pure parsing that always returns the base dev name and (2) a separate sysfs validation step used by correct_hdd_detection.

Suggested change
// Verify this block device exists in sysfs
let sysfs_path = Path::new("/sys/block").join(block_dev);
if sysfs_path.exists() {
Some(block_dev.to_string())
} else {
None
}
Some(block_dev.to_string())

Copilot uses AI. Check for mistakes.
Comment on lines +129 to +142
// We can only fully verify cases where the block device exists in sysfs.
// For non-existent devices, extract_block_device_name returns None because
// the sysfs path check fails. So we just verify it doesn't panic.
if let Some(expected_name) = expected {
if std::path::Path::new("/sys/block")
.join(expected_name)
.exists()
{
assert_eq!(result.as_deref(), Some(*expected_name));
}
}
}
}

Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These Linux tests are highly environment-dependent and can silently do no assertions (e.g., they only assert when /sys/block/<name> exists, and the VirtIO checks are skipped unless /sys/block/vda/... exists). This risks CI passing without exercising the new logic. Consider creating hermetic tests by factoring sysfs access behind an injectable root path (or helper functions) and using a tempdir + symlinks to simulate /sys/block/<dev>/device/driver.

Suggested change
// We can only fully verify cases where the block device exists in sysfs.
// For non-existent devices, extract_block_device_name returns None because
// the sysfs path check fails. So we just verify it doesn't panic.
if let Some(expected_name) = expected {
if std::path::Path::new("/sys/block")
.join(expected_name)
.exists()
{
assert_eq!(result.as_deref(), Some(*expected_name));
}
}
}
}
// Verify that extract_block_device_name parses the device path as expected.
// This test is hermetic: it only checks the returned block device name,
// independent of whether the device actually exists in sysfs.
assert_eq!(result.as_deref(), *expected);
}
}
#[test]
fn test_is_virtual_block_device_on_virtio() {
// On this VirtIO environment, vda should be detected as virtual
if std::path::Path::new("/sys/block/vda/device/driver").exists() {
assert!(
is_virtual_block_device("vda"),
"vda should be detected as a virtual block device"
);
}
}
#[test]
fn test_extract_and_check_real_disks() {
// Integration test: verify that VirtIO disks on this system are correctly identified
use sysinfo::Disks;
let disks = Disks::new_with_refreshed_list();
for disk in disks.list() {
let name = disk.name().to_str().unwrap_or_default();
if let Some(block_dev) = extract_block_device_name(name) {
if block_dev.starts_with("vd") {
assert!(
is_virtual_block_device(&block_dev),
"VirtIO device {block_dev} should be detected as virtual"
);
}
}
}
}
}
}
}

Copilot uses AI. Check for mistakes.
Comment on lines +153 to +170

#[test]
fn test_extract_and_check_real_disks() {
// Integration test: verify that VirtIO disks on this system are correctly identified
use sysinfo::Disks;
let disks = Disks::new_with_refreshed_list();
for disk in disks.list() {
let name = disk.name().to_str().unwrap_or_default();
if let Some(block_dev) = extract_block_device_name(name) {
if block_dev.starts_with("vd") {
assert!(
is_virtual_block_device(&block_dev),
"VirtIO device {block_dev} should be detected as virtual"
);
}
}
}
}
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

test_extract_and_check_real_disks only asserts for devices starting with vd, and otherwise does nothing. On most CI/hosts (where disks are sd*/nvme*), this test will always pass without validating the intended regression fix. Consider replacing this with a deterministic test that constructs a fake sysfs tree and verifies correct_hdd_detection (or is_virtual_block_device) for each supported driver name.

Suggested change
#[test]
fn test_extract_and_check_real_disks() {
// Integration test: verify that VirtIO disks on this system are correctly identified
use sysinfo::Disks;
let disks = Disks::new_with_refreshed_list();
for disk in disks.list() {
let name = disk.name().to_str().unwrap_or_default();
if let Some(block_dev) = extract_block_device_name(name) {
if block_dev.starts_with("vd") {
assert!(
is_virtual_block_device(&block_dev),
"VirtIO device {block_dev} should be detected as virtual"
);
}
}
}
}

Copilot uses AI. Check for mistakes.
- Fix Hyper-V driver name: use 'hv_storvsc' (actual kernel name) instead
  of 'hyperv_storvsc'
- Split extract_block_device_name into parse_block_device_name (pure
  parsing, no I/O) and validate_block_device (sysfs check), making the
  parsing logic independently testable
- Update doc comments to document /dev/root handling
- Make tests hermetic: test_parse_block_device_name asserts deterministically
  on all cases without depending on sysfs availability
- Improve test_extract_and_check_real_disks to validate all disk types,
  not just VirtIO

https://claude.ai/code/session_01MHKaZm6DcA9kjBPnW3KN5K
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants