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