This commit is contained in:
2026-02-28 14:54:48 +01:00
parent 4f54fd81ce
commit 1702e7d058
16 changed files with 713 additions and 688 deletions

View File

@@ -1,11 +1,12 @@
use anyhow::{Result, anyhow};
use anyhow::{Result, anyhow, Context};
use std::path::{Path};
use std::fs;
use std::time::{Duration, Instant};
use std::sync::Mutex;
use std::sync::{Mutex, Arc};
use tracing::{debug, warn, info};
use crate::sal::traits::{SensorBus, ActuatorBus, EnvironmentGuard, HardwareWatchdog, PreflightAuditor, AuditStep, AuditError, SafetyStatus, EnvironmentCtx};
use crate::sal::safety::{TdpLimitMicroWatts, FanSpeedPercentage};
use crate::sal::safety::{PowerLimitWatts, FanSpeedPercent};
use crate::sal::heuristic::discovery::SystemFactSheet;
use crate::sal::heuristic::schema::HardwareDb;
@@ -13,14 +14,9 @@ pub struct GenericLinuxSal {
ctx: EnvironmentCtx,
fact_sheet: SystemFactSheet,
db: HardwareDb,
suppressed_services: Mutex<Vec<String>>,
last_valid_temp: Mutex<(f32, Instant)>,
current_pl1: Mutex<u64>,
last_energy: Mutex<(u64, Instant)>,
// --- Original State for Restoration ---
original_pl1: Mutex<Option<u64>>,
original_pl2: Mutex<Option<u64>>,
}
impl GenericLinuxSal {
@@ -33,14 +29,11 @@ impl GenericLinuxSal {
Self {
db,
suppressed_services: Mutex::new(Vec::new()),
last_valid_temp: Mutex::new((0.0, Instant::now())),
current_pl1: Mutex::new(15_000_000),
last_energy: Mutex::new((initial_energy, Instant::now())),
fact_sheet: facts,
ctx,
original_pl1: Mutex::new(None),
original_pl2: Mutex::new(None),
}
}
@@ -135,7 +128,6 @@ impl SensorBus for GenericLinuxSal {
}
fn get_throttling_status(&self) -> Result<bool> {
// Fallback: check if any cooling device is active (cur_state > 0)
let cooling_base = self.ctx.sysfs_base.join("sys/class/thermal");
if let Ok(entries) = fs::read_dir(cooling_base) {
for entry in entries.flatten() {
@@ -168,68 +160,37 @@ impl ActuatorBus for GenericLinuxSal {
} else { Ok(()) }
}
fn set_fan_speed(&self, _speed: FanSpeedPercentage) -> Result<()> {
fn set_fan_speed(&self, _speed: FanSpeedPercent) -> Result<()> {
Ok(())
}
fn set_sustained_power_limit(&self, limit: TdpLimitMicroWatts) -> Result<()> {
let rapl_path = self.fact_sheet.rapl_paths.first().ok_or_else(|| anyhow!("No PL1 path"))?;
fs::write(rapl_path.join("constraint_0_power_limit_uw"), limit.as_u64().to_string())?;
*self.current_pl1.lock().unwrap() = limit.as_u64();
fn set_sustained_power_limit(&self, limit: PowerLimitWatts) -> Result<()> {
for rapl_path in &self.fact_sheet.rapl_paths {
let limit_path = rapl_path.join("constraint_0_power_limit_uw");
let enable_path = rapl_path.join("constraint_0_enabled");
fs::write(&limit_path, limit.as_microwatts().to_string())
.with_context(|| format!("Failed to write PL1 to {:?}", limit_path))?;
let _ = fs::write(&enable_path, "1");
}
*self.current_pl1.lock().unwrap() = limit.as_microwatts();
Ok(())
}
fn set_burst_power_limit(&self, limit: TdpLimitMicroWatts) -> Result<()> {
let rapl_path = self.fact_sheet.rapl_paths.first().ok_or_else(|| anyhow!("No PL2 path"))?;
fs::write(rapl_path.join("constraint_1_power_limit_uw"), limit.as_u64().to_string())?;
fn set_burst_power_limit(&self, limit: PowerLimitWatts) -> Result<()> {
for rapl_path in &self.fact_sheet.rapl_paths {
let limit_path = rapl_path.join("constraint_1_power_limit_uw");
let enable_path = rapl_path.join("constraint_1_enabled");
fs::write(&limit_path, limit.as_microwatts().to_string())
.with_context(|| format!("Failed to write PL2 to {:?}", limit_path))?;
let _ = fs::write(&enable_path, "1");
}
Ok(())
}
}
impl EnvironmentGuard for GenericLinuxSal {
fn suppress(&self) -> Result<()> {
// Snapshot Power Limits
if let Some(rapl_path) = self.fact_sheet.rapl_paths.first() {
if let Ok(pl1) = fs::read_to_string(rapl_path.join("constraint_0_power_limit_uw")) {
*self.original_pl1.lock().unwrap() = pl1.trim().parse().ok();
}
if let Ok(pl2) = fs::read_to_string(rapl_path.join("constraint_1_power_limit_uw")) {
*self.original_pl2.lock().unwrap() = pl2.trim().parse().ok();
}
}
let mut suppressed = self.suppressed_services.lock().unwrap();
for conflict_id in &self.fact_sheet.active_conflicts {
if let Some(conflict) = self.db.conflicts.iter().find(|c| &c.id == conflict_id) {
for service in &conflict.services {
if self.ctx.runner.run("systemctl", &["is-active", "--quiet", service]).is_ok() {
let _ = self.ctx.runner.run("systemctl", &["stop", service]);
suppressed.push(service.clone());
}
}
}
}
Ok(())
}
fn restore(&self) -> Result<()> {
// Restore Power Limits
if let Some(rapl_path) = self.fact_sheet.rapl_paths.first() {
if let Some(pl1) = *self.original_pl1.lock().unwrap() {
let _ = fs::write(rapl_path.join("constraint_0_power_limit_uw"), pl1.to_string());
}
if let Some(pl2) = *self.original_pl2.lock().unwrap() {
let _ = fs::write(rapl_path.join("constraint_1_power_limit_uw"), pl2.to_string());
}
}
let mut suppressed = self.suppressed_services.lock().unwrap();
for service in suppressed.drain(..) {
let _ = self.ctx.runner.run("systemctl", &["start", &service]);
}
if self.is_dell() { let _ = self.set_fan_mode("auto"); }
Ok(())
}
fn suppress(&self) -> Result<()> { Ok(()) }
fn restore(&self) -> Result<()> { Ok(()) }
}
impl HardwareWatchdog for GenericLinuxSal {
@@ -245,7 +206,3 @@ impl HardwareWatchdog for GenericLinuxSal {
Ok(SafetyStatus::Nominal)
}
}
impl Drop for GenericLinuxSal {
fn drop(&mut self) { let _ = self.restore(); }
}