xref: /aosp_15_r20/external/crosvm/devices/src/virtcpufreq.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
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 anyhow::Context;
13 use serde::Deserialize;
14 use serde::Serialize;
15 use sync::Mutex;
16 
17 use crate::pci::CrosvmDeviceId;
18 use crate::BusAccessInfo;
19 use crate::BusDevice;
20 use crate::DeviceId;
21 use crate::Suspendable;
22 
23 const CPUFREQ_GOV_SCALE_FACTOR_DEFAULT: u32 = 100;
24 const CPUFREQ_GOV_SCALE_FACTOR_SCHEDUTIL: u32 = 80;
25 const SCHED_SCALE_CAPACITY: u32 = 1024;
26 
27 const SCHED_FLAG_RESET_ON_FORK: u64 = 0x1;
28 const SCHED_FLAG_KEEP_POLICY: u64 = 0x08;
29 const SCHED_FLAG_KEEP_PARAMS: u64 = 0x10;
30 const SCHED_FLAG_UTIL_CLAMP_MIN: u64 = 0x20;
31 const SCHED_FLAG_UTIL_CLAMP_MAX: u64 = 0x40;
32 
33 const SCHED_FLAG_KEEP_ALL: u64 = SCHED_FLAG_KEEP_POLICY | SCHED_FLAG_KEEP_PARAMS;
34 
35 #[derive(Serialize, Deserialize)]
36 pub struct VirtCpufreq {
37     pcpu_fmax: u32,
38     pcpu_capacity: u32,
39     vcpu_fmax: u32,
40     vcpu_capacity: u32,
41     vcpu_relative_capacity: u32,
42     pcpu: u32,
43     util_factor: u32,
44 }
45 
get_cpu_info(cpu_id: u32, property: &str) -> Result<u32, Error>46 fn get_cpu_info(cpu_id: u32, property: &str) -> Result<u32, Error> {
47     let path = format!("/sys/devices/system/cpu/cpu{cpu_id}/{property}");
48     std::fs::read_to_string(path)?
49         .trim()
50         .parse()
51         .map_err(|_| Error::new(libc::EINVAL))
52 }
53 
get_cpu_info_str(cpu_id: u32, property: &str) -> Result<String, Error>54 fn get_cpu_info_str(cpu_id: u32, property: &str) -> Result<String, Error> {
55     let path = format!("/sys/devices/system/cpu/cpu{cpu_id}/{property}");
56     std::fs::read_to_string(path).map_err(|_| Error::new(libc::EINVAL))
57 }
58 
get_cpu_capacity(cpu_id: u32) -> Result<u32, Error>59 fn get_cpu_capacity(cpu_id: u32) -> Result<u32, Error> {
60     get_cpu_info(cpu_id, "cpu_capacity")
61 }
62 
get_cpu_maxfreq_khz(cpu_id: u32) -> Result<u32, Error>63 fn get_cpu_maxfreq_khz(cpu_id: u32) -> Result<u32, Error> {
64     get_cpu_info(cpu_id, "cpufreq/cpuinfo_max_freq")
65 }
66 
get_cpu_curfreq_khz(cpu_id: u32) -> Result<u32, Error>67 fn get_cpu_curfreq_khz(cpu_id: u32) -> Result<u32, Error> {
68     get_cpu_info(cpu_id, "cpufreq/scaling_cur_freq")
69 }
70 
handle_read_err(err: Error) -> String71 fn handle_read_err(err: Error) -> String {
72     warn!("Unable to get cpufreq governor, using 100% default util factor. Err: {:?}", err);
73     "unknown_governor".to_string()
74 }
75 
get_cpu_util_factor(cpu_id: u32) -> Result<u32, Error>76 fn get_cpu_util_factor(cpu_id: u32) -> Result<u32, Error> {
77     let gov = get_cpu_info_str(cpu_id, "cpufreq/scaling_governor").unwrap_or_else(handle_read_err);
78     match gov.trim() {
79         "schedutil" => Ok(CPUFREQ_GOV_SCALE_FACTOR_SCHEDUTIL),
80         _ => Ok(CPUFREQ_GOV_SCALE_FACTOR_DEFAULT),
81     }
82 }
83 
84 impl VirtCpufreq {
new(pcpu: u32, cpu_capacity: u32, cpu_fmax: u32) -> Self85     pub fn new(pcpu: u32, cpu_capacity: u32, cpu_fmax: u32) -> Self {
86         let util_factor = get_cpu_util_factor(pcpu).expect("Error getting util factor");
87         let pcpu_capacity = get_cpu_capacity(pcpu).expect("Error reading capacity");
88         let pcpu_fmax = get_cpu_maxfreq_khz(pcpu).expect("Error reading max freq");
89         let vcpu_relative_capacity = u32::try_from((u64::from(cpu_capacity) * u64::from(pcpu_fmax) / u64::from(cpu_fmax))).unwrap();
90 
91         VirtCpufreq {
92             pcpu_fmax,
93             pcpu_capacity,
94             vcpu_fmax: cpu_fmax,
95             vcpu_capacity: cpu_capacity,
96             vcpu_relative_capacity,
97             pcpu,
98             util_factor,
99         }
100     }
101 }
102 
103 impl BusDevice for VirtCpufreq {
device_id(&self) -> DeviceId104     fn device_id(&self) -> DeviceId {
105         CrosvmDeviceId::VirtCpufreq.into()
106     }
107 
debug_label(&self) -> String108     fn debug_label(&self) -> String {
109         "VirtCpufreq Device".to_owned()
110     }
111 
read(&mut self, _info: BusAccessInfo, data: &mut [u8])112     fn read(&mut self, _info: BusAccessInfo, data: &mut [u8]) {
113         if data.len() != std::mem::size_of::<u32>() {
114             warn!(
115                 "{}: unsupported read length {}, only support 4bytes read",
116                 self.debug_label(),
117                 data.len()
118             );
119             return;
120         }
121         // TODO(davidai): Evaluate opening file and re-reading the same fd.
122         let freq = match get_cpu_curfreq_khz(self.pcpu) {
123             Ok(freq) => {
124                 u32::try_from((u64::from(freq) * u64::from(self.pcpu_capacity) / u64::from(self.vcpu_relative_capacity))).unwrap()
125             },
126             Err(e) => panic!("{}: Error reading freq: {}", self.debug_label(), e),
127         };
128 
129         let freq_arr = freq.to_ne_bytes();
130         data.copy_from_slice(&freq_arr);
131     }
132 
write(&mut self, _info: BusAccessInfo, data: &[u8])133     fn write(&mut self, _info: BusAccessInfo, data: &[u8]) {
134         let freq: u32 = match data.try_into().map(u32::from_ne_bytes) {
135             Ok(v) => v,
136             Err(e) => {
137                 warn!(
138                     "{}: unsupported write length {}, only support 4bytes write",
139                     self.debug_label(),
140                     e
141                 );
142                 return;
143             }
144         };
145 
146         // Util margin depends on the cpufreq governor on the host
147         let cpu_cap_scaled = self.vcpu_capacity * self.util_factor / CPUFREQ_GOV_SCALE_FACTOR_DEFAULT;
148         let util = u32::try_from(u64::from(cpu_cap_scaled) * u64::from(freq) / u64::from(self.vcpu_fmax)).unwrap();
149 
150         let mut sched_attr = sched_attr::default();
151         sched_attr.sched_flags =
152             SCHED_FLAG_KEEP_ALL | SCHED_FLAG_UTIL_CLAMP_MIN | SCHED_FLAG_UTIL_CLAMP_MAX | SCHED_FLAG_RESET_ON_FORK;
153         sched_attr.sched_util_min = util;
154 
155         if self.vcpu_fmax != self.pcpu_fmax {
156             sched_attr.sched_util_max = util;
157         } else {
158             sched_attr.sched_util_max = 1024;
159         }
160 
161         if let Err(e) = sched_setattr(0, &mut sched_attr, 0) {
162             panic!("{}: Error setting util value: {}", self.debug_label(), e);
163         }
164     }
165 }
166 
167 impl Suspendable for VirtCpufreq {
168     // Device only active through MMIO writes. Vcpus are frozen before the device tries to sleep,
169     // so the device will not be active at time of calling function.
sleep(&mut self) -> anyhow::Result<()>170     fn sleep(&mut self) -> anyhow::Result<()> {
171         Ok(())
172     }
173 
wake(&mut self) -> anyhow::Result<()>174     fn wake(&mut self) -> anyhow::Result<()> {
175         Ok(())
176     }
177 
snapshot(&mut self) -> anyhow::Result<serde_json::Value>178     fn snapshot(&mut self) -> anyhow::Result<serde_json::Value> {
179         serde_json::to_value(&self).with_context(|| format!("failed to serialize"))
180     }
181 
restore(&mut self, data: serde_json::Value) -> anyhow::Result<()>182     fn restore(&mut self, data: serde_json::Value) -> anyhow::Result<()> {
183         let deser: Self = serde_json::from_value(data).with_context(|| format!("failed to deserialize"))?;
184         *self = deser;
185         Ok(())
186     }
187 }
188