redbear-power: synthetic P-state table from cpuinfo min-max, freq-based index matching
- acpi.rs: fallback creates 6 evenly-spaced P-states from cpuinfo_min_freq / cpuinfo_max_freq when scaling_available_frequencies is absent (intel_pstate, amd-pstate drivers) - platform.rs: probe accepts cpuinfo_max_freq as valid PSS source - app.rs: match current frequency against synthetic P-state table to compute current_idx without MSR access - pss_source label: 'sysfs (cpuinfo min-max)' for intel_pstate
This commit is contained in:
@@ -241,9 +241,38 @@ pub fn read_acpi_pss(cpu: u32) -> Vec<PState> {
|
||||
return out;
|
||||
}
|
||||
}
|
||||
// No real data anywhere. Return empty so the render layer can show
|
||||
// "—" instead of fake numbers. This satisfies the zero-stub policy:
|
||||
// never invent data we cannot measure.
|
||||
// No real data anywhere. Synthesize a P-state table from min/max
|
||||
// frequency range. intel_pstate and similar drivers expose
|
||||
// cpuinfo_min_freq / cpuinfo_max_freq but not the discrete
|
||||
// scaling_available_frequencies list (that's acpi-cpufreq only).
|
||||
// cross-referenced from cpu-x: it reads current frequency from
|
||||
// scaling_cur_freq and min/max from cpuinfo_min/max.
|
||||
let min_path = format!(
|
||||
"/sys/devices/system/cpu/cpu{}/cpufreq/cpuinfo_min_freq",
|
||||
cpu
|
||||
);
|
||||
let max_path = format!(
|
||||
"/sys/devices/system/cpu/cpu{}/cpufreq/cpuinfo_max_freq",
|
||||
cpu
|
||||
);
|
||||
if let (Ok(min_s), Ok(max_s)) =
|
||||
(fs::read_to_string(&min_path), fs::read_to_string(&max_path))
|
||||
{
|
||||
if let (Ok(min_khz), Ok(max_khz)) =
|
||||
(min_s.trim().parse::<u32>(), max_s.trim().parse::<u32>())
|
||||
{
|
||||
if max_khz > min_khz {
|
||||
let steps: u32 = 5;
|
||||
let mut out = Vec::with_capacity(steps as usize + 1);
|
||||
for i in 0..=steps {
|
||||
let freq = min_khz + (max_khz - min_khz) * i / steps;
|
||||
let ctl = (i as u64) << 8;
|
||||
out.push(PState { freq_khz: freq, power_mw: 0, ctl });
|
||||
}
|
||||
return out;
|
||||
}
|
||||
}
|
||||
}
|
||||
Vec::new()
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
use std::collections::VecDeque;
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::time::{Duration, Instant};
|
||||
@@ -326,9 +327,15 @@ impl App {
|
||||
})
|
||||
.collect();
|
||||
let pss_source = if probes.acpi_pss.is_some() {
|
||||
"ACPI _PSS / sysfs".to_string()
|
||||
if Path::new("/scheme/acpi/processor/CPU0/pss").exists() {
|
||||
"ACPI _PSS".to_string()
|
||||
} else if Path::new("/sys/devices/system/cpu/cpu0/cpufreq/scaling_available_frequencies").exists() {
|
||||
"sysfs (available frequencies)".to_string()
|
||||
} else {
|
||||
"sysfs (cpuinfo min-max)".to_string()
|
||||
}
|
||||
} else {
|
||||
"fallback table (no ACPI _PSS / sysfs)".to_string()
|
||||
"unavailable".to_string()
|
||||
};
|
||||
let msr_available = probes.msr.is_some();
|
||||
// Backward-compat aliases: these are the legacy "cpufreqd / thermald"
|
||||
@@ -626,6 +633,18 @@ impl App {
|
||||
row.current_idx = None;
|
||||
row.freq_khz = read_cpu_freq_khz_sysfs(row.id).unwrap_or(0);
|
||||
row.current_power_mw = None;
|
||||
// Match current frequency against synthetic P-state table
|
||||
// to determine which P-state we're in (intel_pstate fallback).
|
||||
if row.freq_khz > 0 && !row.pstates.is_empty() {
|
||||
row.current_idx = row
|
||||
.pstates
|
||||
.iter()
|
||||
.enumerate()
|
||||
.min_by_key(|(_, p)| {
|
||||
(p.freq_khz as i64 - row.freq_khz as i64).unsigned_abs()
|
||||
})
|
||||
.map(|(i, _)| i);
|
||||
}
|
||||
}
|
||||
row.load_pct = read_load(row.id, &mut row.prev_load) * 100.0;
|
||||
if row.load_history.len() >= LOAD_HISTORY_LEN {
|
||||
|
||||
@@ -164,16 +164,23 @@ fn probe_acpi_pss(platform: &Platform) -> Option<AcpiPssBackend> {
|
||||
}
|
||||
}
|
||||
Platform::Linux => {
|
||||
// Linux: sysfs exposes scaling_available_frequencies per CPU.
|
||||
// Linux: try discrete frequencies first (acpi-cpufreq),
|
||||
// then min/max range (intel_pstate, amd-pstate).
|
||||
if Path::new("/sys/devices/system/cpu/cpu0/cpufreq/scaling_available_frequencies").exists() {
|
||||
Some(AcpiPssBackend {
|
||||
path_template:
|
||||
"/sys/devices/system/cpu/cpu{}/cpufreq/scaling_available_frequencies".to_string(),
|
||||
})
|
||||
} else if Path::new("/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq").exists() {
|
||||
// intel_pstate: synthesize P-states from min/max range
|
||||
Some(AcpiPssBackend {
|
||||
path_template:
|
||||
"/sys/devices/system/cpu/cpu{}/cpufreq/cpuinfo_max_freq".to_string(),
|
||||
})
|
||||
} else {
|
||||
eprintln!(
|
||||
"redbear-power: data source cpufreq sysfs (Linux): \
|
||||
/sys/devices/system/cpu/cpu0/cpufreq/scaling_available_frequencies not found; \
|
||||
no scaling_available_frequencies or cpuinfo_max_freq found; \
|
||||
P-state column will read as n/a"
|
||||
);
|
||||
None
|
||||
|
||||
Reference in New Issue
Block a user