Support mdraid hierarchies for storage topology detection.

Signed-off-by: Jason Volk <jason@zemos.net>
This commit is contained in:
Jason Volk
2025-12-24 16:36:38 +00:00
parent e019ed77d0
commit d665a34f30
4 changed files with 188 additions and 54 deletions

View File

@@ -5,7 +5,7 @@ use std::path::PathBuf;
pub use compute::available_parallelism;
use crate::{Result, debug};
use crate::{Result, at, debug};
/// This is needed for opening lots of file descriptors, which tends to
/// happen more often when using RocksDB and making lots of federation
@@ -51,3 +51,13 @@ pub fn current_exe_deleted() -> bool {
.is_some_and(|exe| exe.ends_with(" (deleted)"))
})
}
/// Parse the `KEY=VALUE` contents of a `uevent` file searching for `key` and
/// returning the `value`.
fn _uevent_get<'a>(uevent: &'a str, key: &'a str) -> Option<&'a str> {
uevent
.lines()
.filter_map(|line| line.split_once('='))
.find(|&(key_, _)| key.eq(key_))
.map(at!(1))
}

View File

@@ -9,7 +9,11 @@ type Id = usize;
type Mask = u128;
type Masks = [Mask; MASK_BITS];
const MASK_BITS: usize = 128;
const MASK_BITS: usize = CORES_MAX;
/// Maximum number of cores we support; for now limited to bits of our mask
/// integral.
pub const CORES_MAX: usize = 128;
/// The mask of logical cores available to the process (at startup).
static CORES_AVAILABLE: LazyLock<Mask> = LazyLock::new(|| into_mask(query_cores_available()));

View File

@@ -16,9 +16,22 @@ use crate::{
utils::{result::LogDebugErr, string::SplitInfallible},
};
/// Device characteristics useful for random access throughput
/// Multi-Device (md) i.e. software raid properties.
#[derive(Clone, Debug, Default)]
pub struct Parallelism {
pub struct MultiDevice {
/// Type of raid (i.e. `raid1`); None if no raid present or detected.
pub level: Option<String>,
/// Number of participating devices.
pub raid_disks: usize,
/// The MQ's discovered on the devices; or empty.
pub md: Vec<MultiQueue>,
}
/// Multi-Queue (mq) characteristics.
#[derive(Clone, Debug, Default)]
pub struct MultiQueue {
/// Number of requests for the device.
pub nr_requests: Option<usize>,
@@ -26,7 +39,7 @@ pub struct Parallelism {
pub mq: Vec<Queue>,
}
/// Device queue characteristics
/// Single-queue characteristics
#[derive(Clone, Debug, Default)]
pub struct Queue {
/// Queue's indice.
@@ -39,18 +52,59 @@ pub struct Queue {
pub cpu_list: Vec<usize>,
}
/// Get device characteristics useful for random access throughput by name.
/// Get properties of a MultiDevice (md) storage system
#[must_use]
pub fn parallelism(path: &Path) -> Parallelism {
pub fn md_discover(path: &Path) -> MultiDevice {
let dev_id = dev_from_path(path)
.log_debug_err()
.unwrap_or_default();
let mq_path = block_path(dev_id).join("mq/");
let md_path = block_path(dev_id).join("md/");
let nr_requests_path = block_path(dev_id).join("queue/nr_requests");
let raid_disks_path = md_path.join("raid_disks");
Parallelism {
let raid_disks: usize = read_to_string(&raid_disks_path)
.ok()
.as_deref()
.map(str::trim)
.map(str::parse)
.flat_ok()
.unwrap_or(0);
let single_fallback = raid_disks.eq(&0).then(|| block_path(dev_id));
MultiDevice {
raid_disks,
level: read_to_string(md_path.join("level"))
.ok()
.as_deref()
.map(str::trim)
.map(ToOwned::to_owned),
md: (0..raid_disks)
.map(|i| format!("rd{i}/block"))
.map(|path| md_path.join(&path))
.filter_map(|ref path| path.canonicalize().ok())
.map(|mut path| {
path.pop();
path
})
.chain(single_fallback)
.map(|path| mq_discover(&path))
.filter(|mq| !mq.mq.is_empty())
.collect(),
}
}
/// Get properties of a MultiQueue within a MultiDevice.
#[must_use]
fn mq_discover(path: &Path) -> MultiQueue {
let mq_path = path.join("mq/");
let nr_requests_path = path.join("queue/nr_requests");
MultiQueue {
nr_requests: read_to_string(&nr_requests_path)
.ok()
.as_deref()
@@ -68,13 +122,13 @@ pub fn parallelism(path: &Path) -> Parallelism {
.as_ref()
.is_ok_and(FileType::is_dir)
})
.map(|dir| queue_parallelism(&dir.path()))
.map(|dir| queue_discover(&dir.path()))
.collect(),
}
}
/// Get device queue characteristics by mq path on sysfs(5)
fn queue_parallelism(dir: &Path) -> Queue {
/// Get properties of a Queue within a MultiQueue.
fn queue_discover(dir: &Path) -> Queue {
let queue_id = dir.file_name();
let nr_tags_path = dir.join("nr_tags");