1use std::collections::HashMap;
2use std::fs;
3use std::path::{Path, PathBuf};
4use std::time::Duration;
5
6use nix::unistd::Pid;
7use pathrs::flags::OpenFlags;
8use pathrs::procfs::{ProcfsBase, ProcfsHandle};
9use procfs::{FromRead, ProcError, ProcessCGroups};
10
11use super::blkio::{Blkio, V1BlkioStatsError};
12use super::controller::Controller;
13use super::controller_type::CONTROLLERS;
14use super::cpu::{Cpu, V1CpuStatsError};
15use super::cpuacct::{CpuAcct, V1CpuAcctStatsError};
16use super::cpuset::{CpuSet, V1CpuSetControllerError};
17use super::devices::Devices;
18use super::freezer::{Freezer, V1FreezerControllerError};
19use super::hugetlb::{HugeTlb, V1HugeTlbControllerError, V1HugeTlbStatsError};
20use super::memory::{Memory, V1MemoryControllerError, V1MemoryStatsError};
21use super::network_classifier::NetworkClassifier;
22use super::network_priority::NetworkPriority;
23use super::perf_event::PerfEvent;
24use super::pids::Pids;
25use super::util::V1MountPointError;
26use super::{ControllerType as CtrlType, util};
27use crate::common::{
28 self, AnyCgroupManager, CGROUP_PROCS, CgroupManager, ControllerOpt, FreezerState,
29 JoinSafelyError, PathBufExt, WrapIoResult, WrappedIoError,
30};
31use crate::stats::{PidStatsError, Stats, StatsProvider};
32
33pub struct Manager {
34 subsystems: HashMap<CtrlType, PathBuf>,
35}
36
37#[derive(thiserror::Error, Debug)]
38pub enum V1ManagerError {
39 #[error("io error: {0}")]
40 WrappedIo(#[from] WrappedIoError),
41 #[error("mount point error: {0}")]
42 MountPoint(#[from] V1MountPointError),
43 #[error("proc error: {0}")]
44 Proc(#[from] ProcError),
45 #[error("while joining paths: {0}")]
46 JoinSafely(#[from] JoinSafelyError),
47 #[error("cgroup {0} is required to fulfill the request, but is not supported by this system")]
48 CGroupRequired(CtrlType),
49 #[error("subsystem does not exist")]
50 SubsystemDoesNotExist,
51 #[error(transparent)]
52 Pathrs(#[from] pathrs::error::Error),
53
54 #[error(transparent)]
55 BlkioController(WrappedIoError),
56 #[error(transparent)]
57 CpuController(WrappedIoError),
58 #[error(transparent)]
59 CpuAcctController(WrappedIoError),
60 #[error(transparent)]
61 CpuSetController(#[from] V1CpuSetControllerError),
62 #[error(transparent)]
63 FreezerController(#[from] V1FreezerControllerError),
64 #[error(transparent)]
65 HugeTlbController(#[from] V1HugeTlbControllerError),
66 #[error(transparent)]
67 MemoryController(#[from] V1MemoryControllerError),
68 #[error(transparent)]
69 PidsController(WrappedIoError),
70
71 #[error(transparent)]
72 BlkioStats(#[from] V1BlkioStatsError),
73 #[error(transparent)]
74 CpuStats(#[from] V1CpuStatsError),
75 #[error(transparent)]
76 CpuAcctStats(#[from] V1CpuAcctStatsError),
77 #[error(transparent)]
78 PidsStats(#[from] PidStatsError),
79 #[error(transparent)]
80 HugeTlbStats(#[from] V1HugeTlbStatsError),
81 #[error(transparent)]
82 MemoryStats(#[from] V1MemoryStatsError),
83}
84
85impl Manager {
86 pub fn new(cgroup_path: &Path) -> Result<Self, V1ManagerError> {
88 let mut subsystems = HashMap::new();
89 for subsystem in CONTROLLERS {
90 if let Ok(subsystem_path) = Self::get_subsystem_path(cgroup_path, subsystem) {
91 subsystems.insert(*subsystem, subsystem_path);
92 } else {
93 tracing::warn!("cgroup {} not supported on this system", subsystem);
94 }
95 }
96
97 Ok(Manager { subsystems })
98 }
99
100 fn get_subsystem_path(
101 cgroup_path: &Path,
102 subsystem: &CtrlType,
103 ) -> Result<PathBuf, V1ManagerError> {
104 tracing::debug!("Get path for subsystem: {}", subsystem);
105 let mount_point = util::get_subsystem_mount_point(subsystem)?;
106
107 let cgroup = ProcessCGroups::from_read(ProcfsHandle::new()?.open(
108 ProcfsBase::ProcSelf,
109 "cgroup",
110 OpenFlags::O_RDONLY | OpenFlags::O_CLOEXEC,
111 )?)?
112 .into_iter()
113 .find(|c| c.controllers.contains(&subsystem.to_string()))
114 .ok_or(V1ManagerError::SubsystemDoesNotExist)?;
115
116 let p = if cgroup_path.as_os_str().is_empty() {
117 mount_point.join_safely(Path::new(&cgroup.pathname))?
118 } else {
119 mount_point.join_safely(cgroup_path)?
120 };
121
122 Ok(p)
123 }
124
125 fn get_required_controllers(
126 &self,
127 controller_opt: &ControllerOpt,
128 ) -> Result<HashMap<&CtrlType, &PathBuf>, V1ManagerError> {
129 let mut required_controllers = HashMap::new();
130
131 for controller in CONTROLLERS {
132 let required = match controller {
133 CtrlType::Cpu => Cpu::needs_to_handle(controller_opt).is_some(),
134 CtrlType::CpuAcct => CpuAcct::needs_to_handle(controller_opt).is_some(),
135 CtrlType::CpuSet => CpuSet::needs_to_handle(controller_opt).is_some(),
136 CtrlType::Devices => Devices::needs_to_handle(controller_opt).is_some(),
137 CtrlType::HugeTlb => HugeTlb::needs_to_handle(controller_opt).is_some(),
138 CtrlType::Memory => Memory::needs_to_handle(controller_opt).is_some(),
139 CtrlType::Pids => Pids::needs_to_handle(controller_opt).is_some(),
140 CtrlType::PerfEvent => PerfEvent::needs_to_handle(controller_opt).is_some(),
141 CtrlType::Blkio => Blkio::needs_to_handle(controller_opt).is_some(),
142 CtrlType::NetworkPriority => {
143 NetworkPriority::needs_to_handle(controller_opt).is_some()
144 }
145 CtrlType::NetworkClassifier => {
146 NetworkClassifier::needs_to_handle(controller_opt).is_some()
147 }
148 CtrlType::Freezer => Freezer::needs_to_handle(controller_opt).is_some(),
149 };
150
151 if required {
152 if let Some(subsystem_path) = self.subsystems.get(controller) {
153 required_controllers.insert(controller, subsystem_path);
154 } else {
155 return Err(V1ManagerError::CGroupRequired(*controller));
156 }
157 }
158 }
159
160 Ok(required_controllers)
161 }
162
163 pub fn any(self) -> AnyCgroupManager {
164 AnyCgroupManager::V1(self)
165 }
166}
167
168impl CgroupManager for Manager {
169 type Error = V1ManagerError;
170
171 fn get_all_pids(&self) -> Result<Vec<Pid>, Self::Error> {
172 let devices = self.subsystems.get(&CtrlType::Devices);
173 if let Some(p) = devices {
174 Ok(common::get_all_pids(p)?)
175 } else {
176 Err(V1ManagerError::SubsystemDoesNotExist)
177 }
178 }
179
180 fn add_task(&self, pid: Pid) -> Result<(), Self::Error> {
181 for (ctrl_type, cgroup_path) in &self.subsystems {
182 match ctrl_type {
183 CtrlType::Cpu => Cpu::add_task(pid, cgroup_path)?,
184 CtrlType::CpuAcct => CpuAcct::add_task(pid, cgroup_path)?,
185 CtrlType::CpuSet => CpuSet::add_task(pid, cgroup_path)?,
186 CtrlType::Devices => Devices::add_task(pid, cgroup_path)?,
187 CtrlType::HugeTlb => HugeTlb::add_task(pid, cgroup_path)?,
188 CtrlType::Memory => Memory::add_task(pid, cgroup_path)?,
189 CtrlType::Pids => Pids::add_task(pid, cgroup_path)?,
190 CtrlType::PerfEvent => PerfEvent::add_task(pid, cgroup_path)?,
191 CtrlType::Blkio => Blkio::add_task(pid, cgroup_path)?,
192 CtrlType::NetworkPriority => NetworkPriority::add_task(pid, cgroup_path)?,
193 CtrlType::NetworkClassifier => NetworkClassifier::add_task(pid, cgroup_path)?,
194 CtrlType::Freezer => Freezer::add_task(pid, cgroup_path)?,
195 }
196 }
197
198 Ok(())
199 }
200
201 fn apply(&self, controller_opt: &ControllerOpt) -> Result<(), Self::Error> {
202 for (ctrl_type, cgroup_path) in self.get_required_controllers(controller_opt)? {
203 match ctrl_type {
204 CtrlType::Cpu => Cpu::apply(controller_opt, cgroup_path)?,
205 CtrlType::CpuAcct => CpuAcct::apply(controller_opt, cgroup_path)?,
206 CtrlType::CpuSet => CpuSet::apply(controller_opt, cgroup_path)?,
207 CtrlType::Devices => Devices::apply(controller_opt, cgroup_path)?,
208 CtrlType::HugeTlb => HugeTlb::apply(controller_opt, cgroup_path)?,
209 CtrlType::Memory => Memory::apply(controller_opt, cgroup_path)?,
210 CtrlType::Pids => Pids::apply(controller_opt, cgroup_path)?,
211 CtrlType::PerfEvent => PerfEvent::apply(controller_opt, cgroup_path)?,
212 CtrlType::Blkio => Blkio::apply(controller_opt, cgroup_path)?,
213 CtrlType::NetworkPriority => NetworkPriority::apply(controller_opt, cgroup_path)?,
214 CtrlType::NetworkClassifier => {
215 NetworkClassifier::apply(controller_opt, cgroup_path)?
216 }
217 CtrlType::Freezer => Freezer::apply(controller_opt, cgroup_path)?,
218 }
219 }
220
221 Ok(())
222 }
223
224 fn remove(&self) -> Result<(), Self::Error> {
225 for cgroup_path in self.subsystems.values() {
226 if cgroup_path.exists() {
227 tracing::debug!("remove cgroup {:?}", cgroup_path);
228 let procs_path = cgroup_path.join(CGROUP_PROCS);
229 let procs = fs::read_to_string(&procs_path).wrap_read(&procs_path)?;
230
231 for line in procs.lines() {
232 let pid: i32 = line
233 .parse()
234 .map_err(|err| std::io::Error::new(std::io::ErrorKind::InvalidData, err))
235 .wrap_other(&procs_path)?;
236 let _ = nix::sys::signal::kill(Pid::from_raw(pid), nix::sys::signal::SIGKILL);
237 }
238
239 common::delete_with_retry(cgroup_path, 4, Duration::from_millis(100))?;
240 }
241 }
242
243 Ok(())
244 }
245
246 fn freeze(&self, state: FreezerState) -> Result<(), Self::Error> {
247 let controller_opt = ControllerOpt {
248 resources: &Default::default(),
249 freezer_state: Some(state),
250 oom_score_adj: None,
251 disable_oom_killer: false,
252 };
253 Ok(Freezer::apply(
254 &controller_opt,
255 self.subsystems
256 .get(&CtrlType::Freezer)
257 .ok_or(V1ManagerError::SubsystemDoesNotExist)?,
258 )?)
259 }
260
261 fn stats(&self) -> Result<Stats, Self::Error> {
262 let mut stats = Stats::default();
263
264 for (ctrl_type, cgroup_path) in &self.subsystems {
265 match ctrl_type {
266 CtrlType::Cpu => stats.cpu.throttling = Cpu::stats(cgroup_path)?,
267 CtrlType::CpuAcct => stats.cpu.usage = CpuAcct::stats(cgroup_path)?,
268 CtrlType::Pids => stats.pids = Pids::stats(cgroup_path)?,
269 CtrlType::HugeTlb => stats.hugetlb = HugeTlb::stats(cgroup_path)?,
270 CtrlType::Blkio => stats.blkio = Blkio::stats(cgroup_path)?,
271 CtrlType::Memory => stats.memory = Memory::stats(cgroup_path)?,
272 _ => continue,
273 }
274 }
275
276 Ok(stats)
277 }
278}