feat: intelligent platform detection for cpufreqd and coretempd
cpufreqd: - Read CPU vendor and frequency from /scheme/sys/cpu (CPUID-based) - Generate P-states dynamically from detected max/base frequency - Remove hardcoded 2400-1200 kHz fallback table - Intel SpeedStep and AMD encoding support coretempd: - Detect vendor from /scheme/sys/cpu before MSR probing - Read CPU count from /scheme/sys/cpu for accuracy - Fall back to MSR detection only when platform info unavailable
This commit is contained in:
@@ -53,6 +53,17 @@ fn probe_msr_available() -> bool {
|
||||
}
|
||||
|
||||
fn detect_vendor(cpu: u32) -> Vendor {
|
||||
// Prefer platform info from /scheme/sys/cpu — avoids MSR access
|
||||
if let Ok(d) = fs::read_to_string("/scheme/sys/cpu") {
|
||||
let lower = d.to_lowercase();
|
||||
if lower.contains("intel") || lower.contains("genuineintel") {
|
||||
return Vendor::Intel;
|
||||
}
|
||||
if lower.contains("amd") || lower.contains("authenticamd") {
|
||||
return Vendor::Amd;
|
||||
}
|
||||
}
|
||||
// Fall back to MSR probing if platform info unavailable
|
||||
if read_msr(cpu, IA32_THERM_STATUS).is_some() {
|
||||
Vendor::Intel
|
||||
} else if read_msr(cpu, AMD_TCTL).is_some() {
|
||||
@@ -63,21 +74,27 @@ fn detect_vendor(cpu: u32) -> Vendor {
|
||||
}
|
||||
|
||||
fn detect_cpus() -> Vec<u32> {
|
||||
let mut v = Vec::new();
|
||||
// Try /scheme/sys/cpu first for CPU count
|
||||
if let Ok(d) = fs::read_to_string("/scheme/sys/cpu") {
|
||||
for l in d.lines() {
|
||||
if let Some(id_str) = l.strip_prefix("CPU ") {
|
||||
if let Some((num, _)) = id_str.split_once(':') {
|
||||
if let Ok(id) = num.trim().parse::<u32>() {
|
||||
v.push(id);
|
||||
}
|
||||
let mut v = Vec::new();
|
||||
for line in d.lines() {
|
||||
if line.starts_with("CPUs: ") {
|
||||
if let Ok(count) = line[6..].trim().parse::<u32>() {
|
||||
return (0..count).collect();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if v.is_empty() {
|
||||
v.push(0);
|
||||
// Fall back to /dev/cpu
|
||||
let mut v = Vec::new();
|
||||
if let Ok(e) = fs::read_dir("/dev/cpu") {
|
||||
for x in e.flatten() {
|
||||
if let Ok(n) = x.file_name().into_string() {
|
||||
if let Ok(id) = n.parse() { v.push(id); }
|
||||
}
|
||||
}
|
||||
}
|
||||
if v.is_empty() { v.push(0); }
|
||||
v
|
||||
}
|
||||
|
||||
|
||||
@@ -39,6 +39,53 @@ fn detect_cpus() -> Vec<u32> {
|
||||
v
|
||||
}
|
||||
|
||||
fn read_sys_cpu_info() -> (String, u32, u32) {
|
||||
let mut vendor = String::new();
|
||||
let mut base_mhz: u32 = 0;
|
||||
let mut max_mhz: u32 = 0;
|
||||
if let Ok(d) = fs::read_to_string("/scheme/sys/cpu") {
|
||||
for line in d.lines() {
|
||||
if let Some(v) = line.strip_prefix("Vendor: ") {
|
||||
vendor = v.to_string();
|
||||
} else if let Some(b) = line.strip_prefix("CPU Base MHz: ") {
|
||||
base_mhz = b.parse().unwrap_or(0);
|
||||
} else if let Some(m) = line.strip_prefix("CPU Max MHz: ") {
|
||||
max_mhz = m.parse().unwrap_or(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
(vendor, base_mhz, max_mhz)
|
||||
}
|
||||
|
||||
fn generate_pstates_from_freq(base_mhz: u32, max_mhz: u32, is_intel: bool) -> Vec<PState> {
|
||||
if max_mhz == 0 || base_mhz == 0 {
|
||||
return vec![];
|
||||
}
|
||||
let bus_mhz = if base_mhz > 0 { base_mhz } else { 100 };
|
||||
let max_ratio = max_mhz * 1000 / bus_mhz; // convert to kHz ratio
|
||||
let min_ratio = (max_ratio / 2).max(8); // reasonable minimum
|
||||
|
||||
let mut states = Vec::new();
|
||||
let steps = 4; // generate 4 P-states
|
||||
for i in 0..steps {
|
||||
let ratio = max_ratio - (i * (max_ratio - min_ratio) / (steps - 1));
|
||||
let freq_khz = ratio * bus_mhz;
|
||||
// Intel SpeedStep encoding: ratio in bits 15:8
|
||||
let ctl = if is_intel {
|
||||
((ratio as u64) << 8) & 0xFF00
|
||||
} else {
|
||||
ratio as u64 // AMD uses raw ratio in lower bits
|
||||
};
|
||||
states.push(PState {
|
||||
freq_khz,
|
||||
power_mw: (35000 * ratio / max_ratio) as u32,
|
||||
latency_us: 10,
|
||||
ctl,
|
||||
});
|
||||
}
|
||||
states
|
||||
}
|
||||
|
||||
fn read_acpi_pss(cpu: u32) -> Vec<PState> {
|
||||
let path = format!("/scheme/acpi/processor/CPU{}/pss", cpu);
|
||||
if let Ok(d) = fs::read_to_string(&path) {
|
||||
@@ -53,12 +100,7 @@ fn read_acpi_pss(cpu: u32) -> Vec<PState> {
|
||||
}
|
||||
if !s.is_empty() { return s; }
|
||||
}
|
||||
vec![
|
||||
PState { freq_khz: 2400, power_mw: 35000, latency_us: 10, ctl: 0x1a00 },
|
||||
PState { freq_khz: 2000, power_mw: 25000, latency_us: 10, ctl: 0x1600 },
|
||||
PState { freq_khz: 1600, power_mw: 18000, latency_us: 10, ctl: 0x1200 },
|
||||
PState { freq_khz: 1200, power_mw: 12000, latency_us: 10, ctl: 0x0e00 },
|
||||
]
|
||||
vec![]
|
||||
}
|
||||
|
||||
fn write_msr_raw(cpu: u32, msr: u32, val: u64) -> bool {
|
||||
@@ -156,6 +198,10 @@ fn main() {
|
||||
std::process::exit(if ok { 0 } else { 1 });
|
||||
}
|
||||
|
||||
let (vendor, base_mhz, max_mhz) = read_sys_cpu_info();
|
||||
let is_intel = vendor.to_lowercase().contains("intel");
|
||||
eprintln!("[INFO] cpufreqd: platform {} base={}MHz max={}MHz", vendor, base_mhz, max_mhz);
|
||||
|
||||
let governor = match env::var("CPUFREQ_GOVERNOR").unwrap_or_default().as_str() {
|
||||
"performance" => Governor::Performance, "powersave" => Governor::Powersave,
|
||||
"conservative" => Governor::Conservative, "schedutil" => Governor::Schedutil,
|
||||
@@ -164,11 +210,6 @@ fn main() {
|
||||
let cpus = detect_cpus();
|
||||
eprintln!("[INFO] cpufreqd: detected {} CPU(s), governor={:?}", cpus.len(), governor);
|
||||
|
||||
// Probe MSR availability before attempting any writes.
|
||||
// In environments without MSR 0x199 (QEMU default, some hypervisors),
|
||||
// wrmsr causes a kernel #GP that kills the process. By spawning a child
|
||||
// to test the write first, we detect this safely and degrade to
|
||||
// monitoring-only mode.
|
||||
let msr_ok = cpus.iter().all(|&id| probe_msr_available(id));
|
||||
if !msr_ok {
|
||||
MSR_AVAILABLE.store(false, Ordering::Relaxed);
|
||||
@@ -176,7 +217,10 @@ fn main() {
|
||||
}
|
||||
|
||||
let mut ci: Vec<CpuInfo> = cpus.iter().map(|&id| {
|
||||
let ps = read_acpi_pss(id);
|
||||
let mut ps = read_acpi_pss(id);
|
||||
if ps.is_empty() {
|
||||
ps = generate_pstates_from_freq(base_mhz, max_mhz, is_intel);
|
||||
}
|
||||
eprintln!("[INFO] cpufreqd: CPU{}: {} P-states ({} - {} kHz)", id, ps.len(), ps.first().map_or(0, |p| p.freq_khz), ps.last().map_or(0, |p| p.freq_khz));
|
||||
CpuInfo { id, pstates: ps, current_idx: 0, load_history: [0.0; SAMPLE_WINDOW], load_idx: 0, throttle: false, msr_errors: 0, msr_suppressed: false }
|
||||
}).collect();
|
||||
|
||||
Reference in New Issue
Block a user