use std::fs::File; use std::fs::{self, OpenOptions}; use std::io::{self, Read, Seek, SeekFrom, Write}; use std::ops::{Range, RangeInclusive}; use std::sync::{mpsc, Arc, RwLock}; use std::thread; use std::time::Duration; pub type FrequencyKHZ = u32; #[allow(dead_code)] pub enum Governor { Conservative, Ondemand, Userspace, Powersave, Performance, Schedutil, } impl Governor { fn to_sysfs_string(&self) -> &str { match self { Governor::Conservative => "conservative", Governor::Performance => "performance", Governor::Schedutil => "schedutil", Governor::Powersave => "powersave", Governor::Userspace => "userspace", Governor::Ondemand => "ondemand", } } } #[allow(dead_code)] pub enum Request { UpdatePossibleCPUFrequencyRange, UpdatePolicyCPUFrequency, SetGovernorForCore(u32, Governor), SetFrequencyRangeAllCores(RangeInclusive), SetFrequencyRangeForCore(u32, RangeInclusive), SetTargetFrequencyForCore(u32, FrequencyKHZ), SetTargetFrequencyAllCores(FrequencyKHZ), } pub trait FrequencyService { fn get_possible_cpu_frequency_range(&mut self) -> io::Result>>; fn get_policy_cpu_frequency_ranges(&mut self) -> io::Result>>; fn get_current_frequencies(&mut self) -> io::Result>; fn set_frequency_range_all_cores( &mut self, frequency: &RangeInclusive, ) -> io::Result<()>; fn set_frequency_range_for_core( &mut self, cpu: u32, frequency: &RangeInclusive, ) -> io::Result<()>; fn set_target_frequency_for_core( &mut self, cpu: u32, frequency: FrequencyKHZ, ) -> io::Result<()>; fn set_target_frequency_all_cores(&mut self, frequency: FrequencyKHZ) -> io::Result<()>; } pub struct SysFSFrequencyService { cpus: Range, frequency_ranges: Vec>, cpu_descriptors: Vec, request_receiver: mpsc::Receiver, cpu_frequency_ranges: Arc>>>, policy_frequency_ranges: Arc>>>, cpu_current_frequencies: Arc>>, update_interval: Duration, } struct CPUDescriptors { scaling_min_freq: File, scaling_max_freq: File, scaling_set_speed: File, scaling_cur_freq: File, } impl CPUDescriptors { pub fn new(cpu: u32) -> io::Result { let scaling_min_freq = OpenOptions::new().read(true).write(true).open(format!( "/sys/devices/system/cpu/cpu{}/cpufreq/scaling_min_freq", cpu ))?; let scaling_max_freq = OpenOptions::new().read(true).write(true).open(format!( "/sys/devices/system/cpu/cpu{}/cpufreq/scaling_max_freq", cpu ))?; let scaling_set_speed = OpenOptions::new().write(true).open(format!( "/sys/devices/system/cpu/cpu{}/cpufreq/scaling_setspeed", cpu ))?; let scaling_cur_freq = OpenOptions::new().read(true).open(format!( "/sys/devices/system/cpu/cpu{}/cpufreq/scaling_cur_freq", cpu ))?; Ok(CPUDescriptors { scaling_min_freq, scaling_max_freq, scaling_set_speed, scaling_cur_freq, }) } pub fn new_range(cpus: &Range) -> io::Result> { let mut ret = Vec::new(); for cpu in cpus.clone() { ret.push(Self::new(cpu)?); } Ok(ret) } } impl FrequencyService for SysFSFrequencyService { fn get_possible_cpu_frequency_range( &mut self, ) -> io::Result>> { Ok(self.frequency_ranges.clone()) } fn get_policy_cpu_frequency_ranges(&mut self) -> io::Result>> { let mut ret = Vec::new(); for cpu in &mut self.cpu_descriptors { let mut min_freq = String::new(); cpu.scaling_min_freq.read_to_string(&mut min_freq)?; cpu.scaling_min_freq.seek(SeekFrom::Start(0))?; let mut max_freq = String::new(); cpu.scaling_max_freq.read_to_string(&mut max_freq)?; cpu.scaling_max_freq.seek(SeekFrom::Start(0))?; let min_freq: FrequencyKHZ = min_freq.trim().parse().unwrap(); let max_freq: FrequencyKHZ = max_freq.trim().parse().unwrap(); ret.push(min_freq..=max_freq); } Ok(ret) } fn get_current_frequencies(&mut self) -> io::Result> { let mut ret = Vec::new(); for cpu in &mut self.cpu_descriptors { let mut cur_freq = String::new(); cpu.scaling_cur_freq.read_to_string(&mut cur_freq)?; cpu.scaling_cur_freq.seek(SeekFrom::Start(0))?; let cur_freq: FrequencyKHZ = cur_freq.trim().parse().unwrap(); ret.push(cur_freq); } Ok(ret) } fn set_frequency_range_all_cores( &mut self, frequency: &RangeInclusive, ) -> io::Result<()> { for cpu in self.cpus.clone() { self.set_frequency_range_for_core(cpu, frequency)?; } Ok(()) } fn set_frequency_range_for_core( &mut self, cpu: u32, frequency: &RangeInclusive, ) -> io::Result<()> { self.cpu_descriptors[cpu as usize] .scaling_min_freq .write_fmt(format_args!("{}", frequency.start()))?; self.cpu_descriptors[cpu as usize] .scaling_min_freq .seek(SeekFrom::Start(0))?; self.cpu_descriptors[cpu as usize] .scaling_max_freq .write_fmt(format_args!("{}", frequency.end()))?; self.cpu_descriptors[cpu as usize] .scaling_max_freq .seek(SeekFrom::Start(0))?; Ok(()) } fn set_target_frequency_for_core( &mut self, cpu: u32, frequency: FrequencyKHZ, ) -> io::Result<()> { self.cpu_descriptors[cpu as usize] .scaling_set_speed .write_fmt(format_args!("{}", frequency))?; self.cpu_descriptors[cpu as usize] .scaling_set_speed .seek(SeekFrom::Start(0))?; Ok(()) } fn set_target_frequency_all_cores(&mut self, frequency: FrequencyKHZ) -> io::Result<()> { for cpu in self.cpus.clone() { self.set_target_frequency_for_core(cpu, frequency)?; } Ok(()) } } impl SysFSFrequencyService { pub fn new( cpus: Range, request_receiver: mpsc::Receiver, cpu_frequency_ranges: Arc>>>, policy_frequency_ranges: Arc>>>, cpu_current_frequencies: Arc>>, update_interval: Duration, ) -> io::Result { Ok(SysFSFrequencyService { cpus, frequency_ranges: Vec::new(), cpu_descriptors: Vec::new(), request_receiver, cpu_frequency_ranges, policy_frequency_ranges, cpu_current_frequencies, update_interval, }) } fn run(mut self) { thread::spawn(move || { self.cpu_descriptors = CPUDescriptors::new_range(&self.cpus).unwrap(); let ranges = self.get_freq_limits().unwrap(); self.frequency_ranges = ranges; for cpu in self.cpus.clone() { if self.switch_governor(cpu, Governor::Conservative).is_err() { println!("failed to set governor to conservative"); } } loop { self.handle_requests().unwrap(); *self.cpu_current_frequencies.write().unwrap() = self.get_current_frequencies().unwrap(); thread::sleep(self.update_interval); } }); } fn handle_requests(&mut self) -> io::Result<()> { while let Ok(request) = self.request_receiver.try_recv() { match request { Request::UpdatePossibleCPUFrequencyRange => { *self.cpu_frequency_ranges.write().unwrap() = self.get_possible_cpu_frequency_range()?; } Request::UpdatePolicyCPUFrequency => { *self.policy_frequency_ranges.write().unwrap() = self.get_policy_cpu_frequency_ranges()?; } Request::SetGovernorForCore(cpu, governor) => { self.switch_governor(cpu, governor)?; } Request::SetFrequencyRangeAllCores(frequency) => { self.set_frequency_range_all_cores(&frequency)?; } Request::SetFrequencyRangeForCore(cpu, frequency) => { self.set_frequency_range_for_core(cpu, &frequency)?; } Request::SetTargetFrequencyForCore(cpu, frequency) => { self.set_target_frequency_for_core(cpu, frequency)?; } Request::SetTargetFrequencyAllCores(frequency) => { self.set_target_frequency_all_cores(frequency)?; } } } Ok(()) } fn get_freq_limits(&self) -> io::Result>> { let mut ranges = Vec::new(); for cpu in self.cpus.clone() { let min_freq = fs::read_to_string(format!( "/sys/devices/system/cpu/cpu{}/cpufreq/cpuinfo_min_freq", cpu ))?; let max_freq = fs::read_to_string(format!( "/sys/devices/system/cpu/cpu{}/cpufreq/cpuinfo_max_freq", cpu ))?; let min_freq: FrequencyKHZ = min_freq.trim().parse().unwrap(); let max_freq: FrequencyKHZ = max_freq.trim().parse().unwrap(); ranges.push(min_freq..=max_freq); } Ok(ranges) } fn switch_governor(&self, cpu: u32, governor: Governor) -> io::Result<()> { let governor = governor.to_sysfs_string(); let available_governor = fs::read_to_string("/sys/devices/system/cpu/cpu0/cpufreq/scaling_available_governors")?; if !available_governor.contains(governor) { io::Error::new(io::ErrorKind::NotFound, governor); } let current_governor = fs::read_to_string(format!( "/sys/devices/system/cpu/cpu{}/cpufreq/scaling_governor", cpu ))?; if current_governor != governor { fs::write( format!( "/sys/devices/system/cpu/cpu{}/cpufreq/scaling_governor", cpu ), governor, )?; } Ok(()) } } pub fn start_frequency_service( cpus: Range, cpu_frequency_ranges: Arc>>>, policy_frequency_ranges: Arc>>>, cpu_current_frequencies: Arc>>, update_interval: Duration, ) -> io::Result> { let (request_sender, request_receiver) = mpsc::sync_channel(100); let service = SysFSFrequencyService::new( cpus, request_receiver, cpu_frequency_ranges, policy_frequency_ranges, cpu_current_frequencies, update_interval, )?; service.run(); Ok(request_sender) }