cpufreqd, redbear-power: read CPU count from /scheme/sys/cpu
On Redox the kernel's sys:cpu scheme is a single file (kernel/src/scheme/ sys/cpu.rs) whose contents start with 'CPUs: N\n', not a per-CPU directory. The kernel does not create /dev/cpu/ at all, so the prior read_dir-based enumeration always fell through to the single-CPU fallback on Redox — hiding the fact that the kernel had successfully brought up the APs and reporting only CPU 0 to the governor / power TUI. Read /scheme/sys/cpu and parse the 'CPUs:' line first; fall back to /dev/cpu/ for Linux hosts.
This commit is contained in:
@@ -31,6 +31,17 @@ struct CpuInfo {
|
||||
}
|
||||
|
||||
fn detect_cpus() -> Vec<u32> {
|
||||
// Redox exposes the CPU count via the sys:cpu scheme file, not via a
|
||||
// /dev/cpu/ directory (kernel/src/scheme/sys/cpu.rs).
|
||||
if let Ok(data) = fs::read_to_string("/scheme/sys/cpu") {
|
||||
for line in data.lines() {
|
||||
if let Some(rest) = line.strip_prefix("CPUs: ") {
|
||||
if let Ok(n) = rest.trim().parse::<u32>() {
|
||||
if n > 0 { return (0..n).collect(); }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut v = Vec::new();
|
||||
if let Ok(e) = fs::read_dir("/dev/cpu") {
|
||||
for x in e.flatten() {
|
||||
|
||||
@@ -9,10 +9,12 @@
|
||||
//! missing (QEMU, some laptops), a hard-coded fallback table of
|
||||
//! P0..P5 lets the TUI still display something useful.
|
||||
//!
|
||||
//! - `/scheme/sys/cpu/{n}/` — Redox-native per-CPU directory.
|
||||
//! Falls back to `/dev/cpu` on Linux.
|
||||
//! - `/scheme/sys/cpu` — Redox-native sys scheme file with the CPU
|
||||
//! count. Per-CPU stat files live at `/scheme/sys/cpu/{n}/stat`.
|
||||
//! Linux falls back to `/dev/cpu/`.
|
||||
|
||||
use std::fs;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct PState {
|
||||
@@ -22,23 +24,103 @@ pub struct PState {
|
||||
pub ctl: u64,
|
||||
}
|
||||
|
||||
/// Read per-CPU current frequency in kHz from sysfs cpufreq.
|
||||
/// Falls back to the scaling_cur_freq file on Linux; returns None
|
||||
/// if neither source is available. Used when MSR-based frequency
|
||||
/// reading is unavailable (e.g. no `msr` kernel module).
|
||||
pub fn read_cpu_freq_khz_sysfs(cpu: u32) -> Option<u32> {
|
||||
let path = format!(
|
||||
"/sys/devices/system/cpu/cpu{}/cpufreq/scaling_cur_freq",
|
||||
cpu
|
||||
);
|
||||
if let Ok(s) = fs::read_to_string(&path) {
|
||||
if let Ok(khz) = s.trim().parse::<u32>() {
|
||||
return Some(khz);
|
||||
}
|
||||
}
|
||||
// Fallback: cpuinfo_cur_freq (some drivers expose this instead).
|
||||
let path2 = format!(
|
||||
"/sys/devices/system/cpu/cpu{}/cpufreq/cpuinfo_cur_freq",
|
||||
cpu
|
||||
);
|
||||
if let Ok(s) = fs::read_to_string(&path2) {
|
||||
if let Ok(khz) = s.trim().parse::<u32>() {
|
||||
return Some(khz);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Read package power in microwatts from Intel RAPL powercap sysfs.
|
||||
/// Returns `(power_uw, timestamp)` — the caller should compute delta
|
||||
/// between two calls to get average power in watts.
|
||||
pub fn read_rapl_package_energy() -> Option<(u64, Instant)> {
|
||||
// Intel RAPL: /sys/class/powercap/intel-rapl:0/energy_uj
|
||||
let path = "/sys/class/powercap/intel-rapl:0/energy_uj";
|
||||
if let Ok(s) = fs::read_to_string(path) {
|
||||
if let Ok(uj) = s.trim().parse::<u64>() {
|
||||
return Some((uj, Instant::now()));
|
||||
}
|
||||
}
|
||||
// AMD RAPL: /sys/class/powercap/intel-rapl:0/energy_uj (same path on modern kernels)
|
||||
// Also try the package-level energy file.
|
||||
let path2 = "/sys/class/powercap/intel-rapl/intel-rapl:0/energy_uj";
|
||||
if let Ok(s) = fs::read_to_string(path2) {
|
||||
if let Ok(uj) = s.trim().parse::<u64>() {
|
||||
return Some((uj, Instant::now()));
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Compute average power in watts from two RAPL energy readings.
|
||||
/// Returns `(watts, new_prev)` — the caller should store the returned
|
||||
/// `new_prev` for the next call. Returns `(0.0, prev)` if the delta
|
||||
/// is too small or the readings are stale (>5 seconds).
|
||||
pub fn rapl_power_watts(
|
||||
curr: (u64, Instant),
|
||||
prev: (u64, Instant),
|
||||
) -> (f64, (u64, Instant)) {
|
||||
let (e_curr, t_curr) = curr;
|
||||
let (e_prev, t_prev) = prev;
|
||||
let dt = t_curr.duration_since(t_prev);
|
||||
if dt < Duration::from_millis(100) || dt > Duration::from_secs(5) {
|
||||
return (0.0, curr);
|
||||
}
|
||||
let de = if e_curr >= e_prev {
|
||||
e_curr - e_prev
|
||||
} else {
|
||||
// Energy counter wrapped (unlikely at u64 microjoules, but handle it).
|
||||
0
|
||||
};
|
||||
let watts = (de as f64 / 1_000_000.0) / dt.as_secs_f64();
|
||||
(watts, curr)
|
||||
}
|
||||
|
||||
pub fn detect_cpus() -> Vec<u32> {
|
||||
// Redox exposes CPU enumeration under /scheme/sys/cpu/{n}/...
|
||||
// Linux falls back to /dev/cpu. Probe in that order; if both are
|
||||
// empty, assume a single CPU 0.
|
||||
for root in ["/scheme/sys/cpu", "/dev/cpu"] {
|
||||
if let Ok(entries) = fs::read_dir(root) {
|
||||
let mut v: Vec<u32> = entries
|
||||
.filter_map(|e| e.ok())
|
||||
.filter_map(|e| e.file_name().into_string().ok())
|
||||
.filter_map(|n| n.parse().ok())
|
||||
.collect();
|
||||
v.sort();
|
||||
if !v.is_empty() {
|
||||
return v;
|
||||
// Redox exposes the CPU count via the sys:cpu scheme file
|
||||
// (kernel/src/scheme/sys/cpu.rs) as "CPUs: N\n...". /dev/cpu/ does
|
||||
// not exist on Redox. Linux falls back to /dev/cpu/N entries.
|
||||
if let Ok(data) = fs::read_to_string("/scheme/sys/cpu") {
|
||||
for line in data.lines() {
|
||||
if let Some(rest) = line.strip_prefix("CPUs: ") {
|
||||
if let Ok(n) = rest.trim().parse::<u32>() {
|
||||
if n > 0 { return (0..n).collect(); }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Ok(entries) = fs::read_dir("/dev/cpu") {
|
||||
let mut v: Vec<u32> = entries
|
||||
.filter_map(|e| e.ok())
|
||||
.filter_map(|e| e.file_name().into_string().ok())
|
||||
.filter_map(|n| n.parse().ok())
|
||||
.collect();
|
||||
v.sort();
|
||||
if !v.is_empty() {
|
||||
return v;
|
||||
}
|
||||
}
|
||||
vec![0]
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user