* [PATCH proxmox] proxmox-disks: resolve block devices by mount source
@ 2026-06-02 4:50 Sixia "Leask" Huang
0 siblings, 0 replies; only message in thread
From: Sixia "Leask" Huang @ 2026-06-02 4:50 UTC (permalink / raw)
To: pve-devel; +Cc: Sixia “Leask” Huang
From 68de43f8f461a596ece874c2e45b612e36833faa Mon Sep 17 00:00:00 2001
From: Leask Wong i@leaskh.com
Date: Tue, 2 Jun 2026 00:17:40 -0400
Subject: [PATCH proxmox] proxmox-disks: resolve block devices by mount
source
blockdev_stat_for_path currently relies on st_dev matching the
corresponding mountinfo entry. On btrfs root filesystems backed by
mdraid, stat(“/”) can return a different anonymous device number than
the mountinfo entry, while the mount source still points at the real
block device.
Fall back to the longest matching mount point when the st_dev lookup
does not match, and prefer the mount source block device for the
returned Device. This lets callers read sysfs statistics for the actual
backing block device instead of failing to resolve the path.
This fixes repeated PDM collection log messages on such systems:
failed to collect blockdev statistics for ‘/’: could not determine mounted
device for path /
Add coverage for the btrfs dev-number mismatch and longest-mount
fallback cases.
Signed-off-by: Leask Wong i@leaskh.com
proxmox-disks/src/disks.rs | 149 ++++++++++++++++++++++++++++++++++—
1 file changed, 140 insertions(+), 9 deletions(-)
diff –git a/proxmox-disks/src/disks.rs b/proxmox-disks/src/disks.rs
index a194768..11bdb12 100644
— a/proxmox-disks/src/disks.rs
+++ b/proxmox-disks/src/disks.rs
@@ -9,7 +9,10 @@ use anyhow::{Context as _, Error, format_err};
use libc::dev_t;
use once_cell::sync::OnceCell;
-use proxmox_sys::linux::procfs::{MountInfo, mountinfo::Device};
+use proxmox_sys::linux::procfs::{
+ MountInfo,
+ mountinfo::{Device, Entry},
+};
use crate::{BlockDevStat, Disk};
@@ -132,20 +135,72 @@ impl Disks {
) -> Result<Option<(String, Device, Option)>, Error> {
let meta = std::fs::metadata(path)?;
let device = Device::from_dev_t(meta.dev());
+ let path = std::fs::canonicalize(path).unwrap_or_else(|_|
path.to_path_buf());
- let entry = match Self::find_mount_entry_for_path(self.mount_info()?,
&path, device) {
- Some(entry) => entry,
- None => return Ok(None),
- };
+
- Ok(Some((
- entry.fs_type.clone(),
- Self::device_from_mount_entry(entry),
- entry.mount_source.clone(),
- )))
- }
+
- fn find_mount_entry_for_path<’a>(
- mount_info: &’a MountInfo,
- path: &std::path::Path,
- device: Device,
- ) -> Option<&’a Entry> {
let root_path = std::path::Path::new(“/”);
- let mut device_match = None;
- let mut mount_point_match = None;
+
- for (_id, entry) in mount_info {
- if entry.root != root_path {
- continue;
-
}
-
for (_id, entry) in self.mount_info()? {
- if entry.root == root_path && entry.device == device {
- return Ok(Some((
- entry.fs_type.clone(),
- entry.device,
- entry.mount_source.clone(),
- )));
- if !path.starts_with(&entry.mount_point) {
- continue;
}
+
- if entry.device == device {
- Self::select_longer_mount_point(&mut device_match, entry);
- }
+
-
Self::select_longer_mount_point(&mut mount_point_match, entry);
}
-
Ok(None)
- device_match.or(mount_point_match)
- }
+
- fn select_longer_mount_point<’a>(slot: &mut Option<&’a Entry>, entry:
&’a Entry) {
- let old_len = slot
- .map(|entry| entry.mount_point.as_os_str().len())
- .unwrap_or_default();
- let new_len = entry.mount_point.as_os_str().len();
+
- if new_len > old_len {
- *slot = Some(entry);
- }
- }
+
- fn device_from_mount_entry(entry: &Entry) -> Device {
- if let Some(source) = entry.mount_source.as_deref() {
- let path = Path::new(source);
- if path.is_absolute() {
- if let Ok(meta) = std::fs::metadata(path) {
- if (meta.mode() & libc::S_IFBLK) == libc::S_IFBLK {
- return Device::from_dev_t(meta.rdev());
- }
- }
- }
- }
+
-
entry.device
}
/// Check whether a specific device node is mounted.
@@ -194,3 +249,79 @@ impl Disks {
.ok_or_else(|| format_err!(“could not read disk stats for {}”,
path.as_ref().display()))
}
}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ fn parse_mountinfo(input: &str) -> MountInfo {
+ MountInfo::parse(input.as_bytes()).expect(“failed to parse mountinfo”)
+ }
+
+ fn device(input: &str) -> Device {
+ input.parse().expect(“failed to parse device”)
+ }
+
+ #[test]
+ fn find_mount_entry_prefers_matching_device() {
+ let mount_info = parse_mountinfo(
+ “35 2 8:1 / / rw,relatime - ext4 /dev/sda1 rw\n\
+ 36 35 8:2 / /boot rw,relatime - ext4 /dev/sda2 rw\n”,
+ );
+
+ let entry =
+ Disks::find_mount_entry_for_path(&mount_info, Path::new(“/boot/grub”),
device(“8:2”))
+ .expect(“expected mount entry”);
+
+ assert_eq!(entry.mount_point, Path::new(“/boot”));
+ assert_eq!(entry.device, device(“8:2”));
+ }
+
+ #[test]
+ fn find_mount_entry_falls_back_to_mount_point() {
+ let mount_info =
+ parse_mountinfo(“35 2 0:30 / / rw,relatime - btrfs /dev/md0
rw,subvol=/\n”);
+
+ let entry = Disks::find_mount_entry_for_path(&mount_info,
Path::new(“/”), device(“0:31”))
+ .expect(“expected mount entry”);
+
+ assert_eq!(entry.mount_point, Path::new(“/”));
+ assert_eq!(entry.device, device(“0:30”));
+ }
+
+ #[test]
+ fn find_mount_entry_uses_longest_mount_point_fallback() {
+ let mount_info = parse_mountinfo(
+ “35 2 0:30 / / rw,relatime - btrfs /dev/md0 rw,subvol=/\n\
+ 36 35 0:32 / /srv/data rw,relatime - btrfs /dev/md1 rw,subvol=/data\n”,
+ );
+
+ let entry = Disks::find_mount_entry_for_path(
+ &mount_info,
+ Path::new(“/srv/data/project”),
+ device(“0:33”),
+ )
+ .expect(“expected mount entry”);
+
+ assert_eq!(entry.mount_point, Path::new(“/srv/data”));
+ assert_eq!(entry.device, device(“0:32”));
+ }
+
+ #[test]
+ fn find_mount_entry_ignores_non_root_mountinfo_entries() {
+ let mount_info = parse_mountinfo(
+ “35 2 0:30 / / rw,relatime - btrfs /dev/md0 rw,subvol=/\n\
+ 36 35 0:32 /subvol /srv/data rw,relatime - btrfs /dev/md1 rw\n”,
+ );
+
+ let entry = Disks::find_mount_entry_for_path(
+ &mount_info,
+ Path::new(“/srv/data/project”),
+ device(“0:33”),
+ )
+ .expect(“expected mount entry”);
+
+ assert_eq!(entry.mount_point, Path::new(“/”));
+ assert_eq!(entry.device, device(“0:30”));
+ }
+}
–
2.54.0
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2026-06-02 7:53 UTC | newest]
Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-02 4:50 [PATCH proxmox] proxmox-disks: resolve block devices by mount source Sixia "Leask" Huang
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox