driver-manager: N8 — per-driver [driver.params] parsing + env emission
Closes the v4.8 cross-cutting item 'modprobe.d options parser': the new format's [driver.params] table (e.g. 50-amdgpu.toml's radeon_overlap / amdgpu_force / amdgpu_disable_accel knobs) is now parsed from every [[driver]] entry and emitted to the spawned child as REDBEAR_DRIVER_PARAM_<NAME>=<value> env vars. RawDriverEntry grows a new field: params: BTreeMap<String, String> parsed at load_all time into a Vec<(name, value)> on DriverConfig (preserving BTreeMap's sorted iteration order for deterministic env-var emission). The spawn path applies self.params after the existing [[options]] env-var emission; both share the REDBEAR_DRIVER_PARAM_* prefix so drivers don't need to distinguish the two sources. Matching semantics (radeon_overlap='exclusive', amdgpu_force=true) remain a follow-up — the env-var wiring is the foundational piece, and 50-amdgpu.toml's radeon_overlap knob can be honoured by the radeon driver daemon reading its env-var directly. Tests (2 new): - load_all_parses_driver_params: 3-params TOML entry parses with deterministic sorted iteration - load_all_driver_params_default_empty: a [[driver]] without [driver.params] still parses, params list is empty 162 driver-manager tests pass. driver-manager-audit-no-stubs.py: 46 files, 0 violations.
This commit is contained in:
@@ -46,11 +46,18 @@ pub struct DriverConfig {
|
||||
pub matches: Vec<DriverMatch>,
|
||||
pub depends_on: Vec<String>,
|
||||
pub exclusive_with: Vec<String>,
|
||||
/// Per-driver parameter overrides from the policy layer's
|
||||
/// `[driver.params]` table. Each `(name, value)` pair is emitted
|
||||
/// as `REDBEAR_DRIVER_PARAM_<NAME>=<value>` in the spawn env.
|
||||
/// This is the wiring for `redbear-driver-policy`'s `50-amdgpu.toml`
|
||||
/// style per-driver knobs (radeon_overlap, amdgpu_force,
|
||||
/// amdgpu_disable_accel, etc.).
|
||||
pub params: Vec<(String, String)>,
|
||||
/// Initial PCI power state for the spawned driver daemon. D0 is the
|
||||
/// default and matches the post-`enable_device` state of every bound
|
||||
/// device. D3hot / D3cold can be set for low-power drivers (e.g.
|
||||
/// always-on housekeeping devices) that wake the bus on demand.
|
||||
/// When non-D0, driver-manager sets
|
||||
/// When non-D0, driver-manager emits
|
||||
/// `REDBEAR_DRIVER_INITIAL_POWER_STATE=<state>` in the child's env so
|
||||
/// the daemon can call `set_power_state` on its granted channel.
|
||||
pub initial_power_state: PciPowerState,
|
||||
@@ -102,6 +109,7 @@ impl Clone for DriverConfig {
|
||||
matches: self.matches.clone(),
|
||||
depends_on: self.depends_on.clone(),
|
||||
exclusive_with: self.exclusive_with.clone(),
|
||||
params: self.params.clone(),
|
||||
initial_power_state: self.initial_power_state,
|
||||
spawned: Mutex::new(HashMap::new()),
|
||||
pid_to_device: Mutex::new(HashMap::new()),
|
||||
@@ -201,6 +209,7 @@ command = ["/usr/bin/redbear-acmd"]
|
||||
matches: Vec::new(),
|
||||
depends_on: Vec::new(),
|
||||
exclusive_with: Vec::new(),
|
||||
params: Vec::new(),
|
||||
initial_power_state: PciPowerState::D0,
|
||||
spawned: Mutex::new(HashMap::new()),
|
||||
pid_to_device: Mutex::new(HashMap::new()),
|
||||
@@ -219,6 +228,7 @@ command = ["/usr/bin/redbear-acmd"]
|
||||
matches: Vec::new(),
|
||||
depends_on: Vec::new(),
|
||||
exclusive_with: Vec::new(),
|
||||
params: Vec::new(),
|
||||
initial_power_state: PciPowerState::D0,
|
||||
spawned: Mutex::new(HashMap::new()),
|
||||
pid_to_device: Mutex::new(HashMap::new()),
|
||||
@@ -409,6 +419,84 @@ command = ["e1000d"]
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn load_all_parses_driver_params() {
|
||||
// N8: per-driver [driver.params] table from redbear-driver-policy
|
||||
// (e.g. 50-amdgpu.toml) — each entry becomes a param override
|
||||
// that the spawn path emits as REDBEAR_DRIVER_PARAM_<NAME>=<value>.
|
||||
serialized(|| {
|
||||
let dir = std::env::temp_dir().join(format!(
|
||||
"rb-dm-driver-params-{}-{}",
|
||||
std::process::id(),
|
||||
std::time::SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_nanos()
|
||||
));
|
||||
std::fs::create_dir_all(&dir).unwrap();
|
||||
std::fs::write(
|
||||
dir.join("50-amdgpu.toml"),
|
||||
r#"
|
||||
[[driver]]
|
||||
name = "redox-drm"
|
||||
priority = 95
|
||||
command = ["/usr/bin/redox-drm"]
|
||||
|
||||
[driver.params]
|
||||
radeon_overlap = "exclusive"
|
||||
amdgpu_force = "true"
|
||||
amdgpu_disable_accel = "false"
|
||||
"#,
|
||||
)
|
||||
.unwrap();
|
||||
let configs = DriverConfig::load_all(dir.to_str().unwrap())
|
||||
.expect("[driver.params] must parse");
|
||||
assert_eq!(configs.len(), 1);
|
||||
let params = &configs[0].params;
|
||||
assert_eq!(params.len(), 3);
|
||||
// BTreeMap iteration is sorted by key.
|
||||
assert_eq!(params[0].0, "amdgpu_disable_accel");
|
||||
assert_eq!(params[0].1, "false");
|
||||
assert_eq!(params[1].0, "amdgpu_force");
|
||||
assert_eq!(params[1].1, "true");
|
||||
assert_eq!(params[2].0, "radeon_overlap");
|
||||
assert_eq!(params[2].1, "exclusive");
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn load_all_driver_params_default_empty() {
|
||||
// A driver entry without [driver.params] should still parse
|
||||
// and have an empty params list — drivers that don't need
|
||||
// knobs aren't required to declare any.
|
||||
serialized(|| {
|
||||
let dir = std::env::temp_dir().join(format!(
|
||||
"rb-dm-driver-params-empty-{}-{}",
|
||||
std::process::id(),
|
||||
std::time::SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_nanos()
|
||||
));
|
||||
std::fs::create_dir_all(&dir).unwrap();
|
||||
std::fs::write(
|
||||
dir.join("10-foo.toml"),
|
||||
r#"
|
||||
[[driver]]
|
||||
name = "foo"
|
||||
priority = 80
|
||||
command = ["/usr/bin/foo"]
|
||||
"#,
|
||||
)
|
||||
.unwrap();
|
||||
let configs = DriverConfig::load_all(dir.to_str().unwrap()).unwrap();
|
||||
assert_eq!(configs.len(), 1);
|
||||
assert!(configs[0].params.is_empty());
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn load_all_warns_and_defaults_on_invalid_initial_power_state() {
|
||||
serialized(|| {
|
||||
@@ -1108,6 +1196,23 @@ impl DriverConfig {
|
||||
},
|
||||
None => PciPowerState::D0,
|
||||
};
|
||||
// Collect per-driver [driver.params] into an
|
||||
// ordered Vec for env-var emission. BTreeMap
|
||||
// iteration is sorted by key so the env vars
|
||||
// are emitted in deterministic order.
|
||||
let params: Vec<(String, String)> = driver
|
||||
.params
|
||||
.iter()
|
||||
.map(|(k, v)| (k.clone(), v.clone()))
|
||||
.collect();
|
||||
if !params.is_empty() {
|
||||
log::info!(
|
||||
"policy: driver `{}` has {} [driver.params] override{}",
|
||||
driver.name,
|
||||
params.len(),
|
||||
if params.len() == 1 { "" } else { "s" }
|
||||
);
|
||||
}
|
||||
configs.push(DriverConfig {
|
||||
name: driver.name,
|
||||
description: driver.description,
|
||||
@@ -1116,6 +1221,7 @@ impl DriverConfig {
|
||||
matches,
|
||||
depends_on: driver.depends_on,
|
||||
exclusive_with: driver.exclusive_with,
|
||||
params,
|
||||
initial_power_state,
|
||||
spawned: Mutex::new(HashMap::new()),
|
||||
pid_to_device: Mutex::new(HashMap::new()),
|
||||
@@ -1475,6 +1581,18 @@ impl Driver for DriverConfig {
|
||||
);
|
||||
}
|
||||
|
||||
// N8: apply per-driver [driver.params] overrides from the
|
||||
// policy layer (e.g. 50-amdgpu.toml's radeon_overlap /
|
||||
// amdgpu_force / amdgpu_disable_accel). Same env-var
|
||||
// convention as `[[options]]` so drivers don't need to
|
||||
// distinguish the two sources.
|
||||
for (param_name, param_value) in &self.params {
|
||||
cmd.env(
|
||||
format!("REDBEAR_DRIVER_PARAM_{}", param_name.to_uppercase()),
|
||||
param_value,
|
||||
);
|
||||
}
|
||||
|
||||
if !self.initial_power_state.is_default() {
|
||||
cmd.env(
|
||||
"REDBEAR_DRIVER_INITIAL_POWER_STATE",
|
||||
@@ -1753,6 +1871,15 @@ struct RawDriverEntry {
|
||||
exclusive_with: Vec<String>,
|
||||
#[serde(default)]
|
||||
initial_power_state: Option<String>,
|
||||
/// Per-driver parameter overrides from the policy layer's
|
||||
/// `[driver.params]` table. Each entry becomes a
|
||||
/// `REDBEAR_DRIVER_PARAM_<NAME>=<value>` env var on the spawned
|
||||
/// child's environment. The schema is a flat string-to-string map;
|
||||
/// values are stored verbatim (the driver daemon interprets them
|
||||
/// per its own schema). Used by `redbear-driver-policy`'s
|
||||
/// `50-amdgpu.toml` and similar per-driver policy files.
|
||||
#[serde(default)]
|
||||
params: std::collections::BTreeMap<String, String>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
@@ -1895,6 +2022,7 @@ fn convert_legacy(legacy: RawLegacyEntry) -> DriverConfig {
|
||||
matches,
|
||||
depends_on: Vec::new(),
|
||||
exclusive_with: legacy.exclusive_with,
|
||||
params: Vec::new(),
|
||||
initial_power_state: PciPowerState::D0,
|
||||
spawned: Mutex::new(HashMap::new()),
|
||||
pid_to_device: Mutex::new(HashMap::new()),
|
||||
|
||||
Reference in New Issue
Block a user