• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2023 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 use base::sched_attr;
6 use base::sched_setattr;
7 use base::warn;
8 use base::Error;
9 use std::os::unix::net::UnixStream;
10 use std::sync::Arc;
11 
12 use sync::Mutex;
13 
14 use crate::pci::CrosvmDeviceId;
15 use crate::BusAccessInfo;
16 use crate::BusDevice;
17 use crate::DeviceId;
18 use crate::Suspendable;
19 
20 const CPUFREQ_GOV_SCALE_FACTOR_DEFAULT: u32 = 100;
21 const CPUFREQ_GOV_SCALE_FACTOR_SCHEDUTIL: u32 = 80;
22 
23 const SCHED_FLAG_RESET_ON_FORK: u64 = 0x1;
24 const SCHED_FLAG_KEEP_POLICY: u64 = 0x08;
25 const SCHED_FLAG_KEEP_PARAMS: u64 = 0x10;
26 const SCHED_FLAG_UTIL_CLAMP_MIN: u64 = 0x20;
27 
28 const SCHED_FLAG_KEEP_ALL: u64 = SCHED_FLAG_KEEP_POLICY | SCHED_FLAG_KEEP_PARAMS;
29 
30 pub struct VirtCpufreq {
31     cpu_fmax: u32,
32     cpu_capacity: u32,
33     pcpu: u32,
34     util_factor: u32,
35 }
36 
get_cpu_info(cpu_id: u32, property: &str) -> Result<u32, Error>37 fn get_cpu_info(cpu_id: u32, property: &str) -> Result<u32, Error> {
38     let path = format!("/sys/devices/system/cpu/cpu{cpu_id}/{property}");
39     std::fs::read_to_string(path)?
40         .trim()
41         .parse()
42         .map_err(|_| Error::new(libc::EINVAL))
43 }
44 
get_cpu_info_str(cpu_id: u32, property: &str) -> Result<String, Error>45 fn get_cpu_info_str(cpu_id: u32, property: &str) -> Result<String, Error> {
46     let path = format!("/sys/devices/system/cpu/cpu{cpu_id}/{property}");
47     std::fs::read_to_string(path).map_err(|_| Error::new(libc::EINVAL))
48 }
49 
get_cpu_capacity(cpu_id: u32) -> Result<u32, Error>50 fn get_cpu_capacity(cpu_id: u32) -> Result<u32, Error> {
51     get_cpu_info(cpu_id, "cpu_capacity")
52 }
53 
get_cpu_maxfreq_khz(cpu_id: u32) -> Result<u32, Error>54 fn get_cpu_maxfreq_khz(cpu_id: u32) -> Result<u32, Error> {
55     get_cpu_info(cpu_id, "cpufreq/cpuinfo_max_freq")
56 }
57 
get_cpu_curfreq_khz(cpu_id: u32) -> Result<u32, Error>58 fn get_cpu_curfreq_khz(cpu_id: u32) -> Result<u32, Error> {
59     get_cpu_info(cpu_id, "cpufreq/scaling_cur_freq")
60 }
61 
handle_read_err(err: Error) -> String62 fn handle_read_err(err: Error) -> String {
63     warn!("Unable to get cpufreq governor, using 100% default util factor. Err: {:?}", err);
64     "unknown_governor".to_string()
65 }
66 
get_cpu_util_factor(cpu_id: u32) -> Result<u32, Error>67 fn get_cpu_util_factor(cpu_id: u32) -> Result<u32, Error> {
68     let gov = get_cpu_info_str(cpu_id, "cpufreq/scaling_governor").unwrap_or_else(handle_read_err);
69     match gov.trim() {
70         "schedutil" => Ok(CPUFREQ_GOV_SCALE_FACTOR_SCHEDUTIL),
71         _ => Ok(CPUFREQ_GOV_SCALE_FACTOR_DEFAULT),
72     }
73 }
74 
75 impl VirtCpufreq {
new(pcpu: u32, _socket: Option<Arc<Mutex<UnixStream>>>) -> Self76     pub fn new(pcpu: u32, _socket: Option<Arc<Mutex<UnixStream>>>) -> Self {
77         let cpu_capacity = get_cpu_capacity(pcpu).expect("Error reading capacity");
78         let cpu_fmax = get_cpu_maxfreq_khz(pcpu).expect("Error reading max freq");
79         let util_factor = get_cpu_util_factor(pcpu).expect("Error getting util factor");
80 
81         VirtCpufreq {
82             cpu_fmax,
83             cpu_capacity,
84             pcpu,
85             util_factor,
86         }
87     }
88 }
89 
90 impl BusDevice for VirtCpufreq {
device_id(&self) -> DeviceId91     fn device_id(&self) -> DeviceId {
92         CrosvmDeviceId::VirtCpufreq.into()
93     }
94 
debug_label(&self) -> String95     fn debug_label(&self) -> String {
96         "VirtCpufreq Device".to_owned()
97     }
98 
read(&mut self, _info: BusAccessInfo, data: &mut [u8])99     fn read(&mut self, _info: BusAccessInfo, data: &mut [u8]) {
100         if data.len() != std::mem::size_of::<u32>() {
101             warn!(
102                 "{}: unsupported read length {}, only support 4bytes read",
103                 self.debug_label(),
104                 data.len()
105             );
106             return;
107         }
108         // TODO(davidai): Evaluate opening file and re-reading the same fd.
109         let freq = match get_cpu_curfreq_khz(self.pcpu) {
110             Ok(freq) => freq,
111             Err(e) => panic!("{}: Error reading freq: {}", self.debug_label(), e),
112         };
113 
114         let freq_arr = freq.to_ne_bytes();
115         data.copy_from_slice(&freq_arr);
116     }
117 
write(&mut self, _info: BusAccessInfo, data: &[u8])118     fn write(&mut self, _info: BusAccessInfo, data: &[u8]) {
119         let freq: u32 = match data.try_into().map(u32::from_ne_bytes) {
120             Ok(v) => v,
121             Err(e) => {
122                 warn!(
123                     "{}: unsupported write length {}, only support 4bytes write",
124                     self.debug_label(),
125                     e
126                 );
127                 return;
128             }
129         };
130 
131         // Util margin depends on the cpufreq governor on the host
132         let cpu_cap_scaled = self.cpu_capacity * self.util_factor / CPUFREQ_GOV_SCALE_FACTOR_DEFAULT;
133         let util = cpu_cap_scaled * freq / self.cpu_fmax;
134 
135         let mut sched_attr = sched_attr::default();
136         sched_attr.sched_flags =
137             SCHED_FLAG_KEEP_ALL | SCHED_FLAG_UTIL_CLAMP_MIN | SCHED_FLAG_RESET_ON_FORK;
138         sched_attr.sched_util_min = util;
139 
140         if let Err(e) = sched_setattr(0, &mut sched_attr, 0) {
141             panic!("{}: Error setting util value: {}", self.debug_label(), e);
142         }
143     }
144 }
145 
146 impl Suspendable for VirtCpufreq {}
147