1use std::fs::{self};
2use std::os::unix::fs::PermissionsExt;
3use std::path::Component::RootDir;
4use std::path::{Path, PathBuf};
5use std::time::Duration;
6
7use nix::unistd::Pid;
8
9use super::controller::Controller;
10use super::controller_type::{
11 CONTROLLER_TYPES, ControllerType, PSEUDO_CONTROLLER_TYPES, PseudoControllerType,
12};
13use super::cpu::{Cpu, V2CpuControllerError, V2CpuStatsError};
14use super::cpuset::CpuSet;
15#[cfg(feature = "cgroupsv2_devices")]
16use super::devices::Devices;
17use super::freezer::{Freezer, V2FreezerError};
18use super::hugetlb::{HugeTlb, V2HugeTlbControllerError, V2HugeTlbStatsError};
19use super::io::{Io, V2IoControllerError, V2IoStatsError};
20use super::memory::{Memory, V2MemoryControllerError, V2MemoryStatsError};
21use super::pids::Pids;
22use super::unified::{Unified, V2UnifiedError};
23use super::util::{self, CGROUP_SUBTREE_CONTROL, V2UtilError};
24use crate::common::{
25 self, AnyCgroupManager, CGROUP_PROCS, CgroupManager, ControllerOpt, FreezerState,
26 JoinSafelyError, PathBufExt, WrapIoResult, WrappedIoError,
27};
28use crate::stats::{PidStatsError, Stats, StatsProvider};
29
30pub const CGROUP_KILL: &str = "cgroup.kill";
31
32#[derive(thiserror::Error, Debug)]
33pub enum V2ManagerError {
34 #[error("io error: {0}")]
35 WrappedIo(#[from] WrappedIoError),
36 #[error("while joining paths: {0}")]
37 JoinSafely(#[from] JoinSafelyError),
38 #[error(transparent)]
39 Util(#[from] V2UtilError),
40
41 #[error(transparent)]
42 CpuController(#[from] V2CpuControllerError),
43 #[error(transparent)]
44 CpuSetController(WrappedIoError),
45 #[error(transparent)]
46 HugeTlbController(#[from] V2HugeTlbControllerError),
47 #[error(transparent)]
48 IoController(#[from] V2IoControllerError),
49 #[error(transparent)]
50 MemoryController(#[from] V2MemoryControllerError),
51 #[error(transparent)]
52 PidsController(WrappedIoError),
53 #[error(transparent)]
54 UnifiedController(#[from] V2UnifiedError),
55 #[error(transparent)]
56 FreezerController(#[from] V2FreezerError),
57 #[cfg(feature = "cgroupsv2_devices")]
58 #[error(transparent)]
59 DevicesController(#[from] super::devices::controller::DevicesControllerError),
60
61 #[error(transparent)]
62 CpuStats(#[from] V2CpuStatsError),
63 #[error(transparent)]
64 HugeTlbStats(#[from] V2HugeTlbStatsError),
65 #[error(transparent)]
66 PidsStats(PidStatsError),
67 #[error(transparent)]
68 MemoryStats(#[from] V2MemoryStatsError),
69 #[error(transparent)]
70 IoStats(#[from] V2IoStatsError),
71}
72
73pub struct Manager {
77 root_path: PathBuf,
78 cgroup_path: PathBuf,
79 full_path: PathBuf,
80}
81
82impl Manager {
83 pub fn new(root_path: PathBuf, cgroup_path: PathBuf) -> Result<Self, V2ManagerError> {
86 let full_path = root_path.join_safely(&cgroup_path)?;
87
88 Ok(Self {
89 root_path,
90 cgroup_path,
91 full_path,
92 })
93 }
94
95 fn create_unified_cgroup(&self, pid: Pid) -> Result<(), V2ManagerError> {
97 let controllers: Vec<String> = util::get_available_controllers(&self.root_path)?
98 .iter()
99 .map(|c| format!("+{c}"))
100 .collect();
101
102 Self::write_controllers(&self.root_path, &controllers)?;
103
104 let mut current_path = self.root_path.clone();
105 let mut components = self
106 .cgroup_path
107 .components()
108 .filter(|c| c.ne(&RootDir))
109 .peekable();
110 while let Some(component) = components.next() {
111 current_path = current_path.join(component);
112 if !current_path.exists() {
113 fs::create_dir(¤t_path).wrap_create_dir(¤t_path)?;
114 fs::metadata(¤t_path)
115 .wrap_other(¤t_path)?
116 .permissions()
117 .set_mode(0o755);
118 }
119
120 if components.peek().is_some() {
123 Self::write_controllers(¤t_path, &controllers)?;
124 }
125 }
126
127 common::write_cgroup_file(self.full_path.join(CGROUP_PROCS), pid)?;
128 Ok(())
129 }
130
131 fn write_controllers(path: &Path, controllers: &[String]) -> Result<(), WrappedIoError> {
133 for controller in controllers {
134 common::write_cgroup_file_str(path.join(CGROUP_SUBTREE_CONTROL), controller)?;
135 }
136
137 Ok(())
138 }
139
140 pub fn any(self) -> AnyCgroupManager {
141 AnyCgroupManager::V2(self)
142 }
143}
144
145impl CgroupManager for Manager {
146 type Error = V2ManagerError;
147
148 fn add_task(&self, pid: Pid) -> Result<(), Self::Error> {
149 if self.full_path.exists() {
150 common::write_cgroup_file(self.full_path.join(CGROUP_PROCS), pid)?;
151 return Ok(());
152 }
153 self.create_unified_cgroup(pid)?;
154 Ok(())
155 }
156
157 fn apply(&self, controller_opt: &ControllerOpt) -> Result<(), Self::Error> {
158 for controller in CONTROLLER_TYPES {
159 match controller {
160 ControllerType::Cpu => Cpu::apply(controller_opt, &self.full_path)?,
161 ControllerType::CpuSet => CpuSet::apply(controller_opt, &self.full_path)?,
162 ControllerType::HugeTlb => HugeTlb::apply(controller_opt, &self.full_path)?,
163 ControllerType::Io => Io::apply(controller_opt, &self.full_path)?,
164 ControllerType::Memory => Memory::apply(controller_opt, &self.full_path)?,
165 ControllerType::Pids => Pids::apply(controller_opt, &self.full_path)?,
166 }
167 }
168
169 #[cfg(feature = "cgroupsv2_devices")]
170 Devices::apply(controller_opt, &self.full_path)?;
171
172 for pseudoctlr in PSEUDO_CONTROLLER_TYPES {
173 if let PseudoControllerType::Unified = pseudoctlr {
174 Unified::apply(
175 controller_opt,
176 &self.full_path,
177 util::get_available_controllers(&self.root_path)?,
178 )?;
179 }
180 }
181
182 Ok(())
183 }
184
185 fn remove(&self) -> Result<(), Self::Error> {
186 if self.full_path.exists() {
187 tracing::debug!("remove cgroup {:?}", self.full_path);
188 let kill_file = self.full_path.join(CGROUP_KILL);
189 if kill_file.exists() {
190 fs::write(&kill_file, "1").wrap_write(&kill_file, "1")?;
191 } else {
192 let procs_path = self.full_path.join(CGROUP_PROCS);
193 let procs = fs::read_to_string(&procs_path).wrap_read(&procs_path)?;
194
195 for line in procs.lines() {
196 let pid: i32 = line
197 .parse()
198 .map_err(|err| std::io::Error::new(std::io::ErrorKind::InvalidData, err))
199 .wrap_other(&procs_path)?;
200 let _ = nix::sys::signal::kill(Pid::from_raw(pid), nix::sys::signal::SIGKILL);
201 }
202 }
203
204 common::delete_with_retry(&self.full_path, 4, Duration::from_millis(100))?;
205 }
206
207 Ok(())
208 }
209
210 fn freeze(&self, state: FreezerState) -> Result<(), Self::Error> {
211 let controller_opt = ControllerOpt {
212 resources: &Default::default(),
213 freezer_state: Some(state),
214 oom_score_adj: None,
215 disable_oom_killer: false,
216 };
217 Ok(Freezer::apply(&controller_opt, &self.full_path)?)
218 }
219
220 fn stats(&self) -> Result<Stats, Self::Error> {
221 let mut stats = Stats::default();
222
223 for subsystem in CONTROLLER_TYPES {
224 match subsystem {
225 ControllerType::Cpu => stats.cpu = Cpu::stats(&self.full_path)?,
226 ControllerType::HugeTlb => stats.hugetlb = HugeTlb::stats(&self.full_path)?,
227 ControllerType::Pids => {
228 stats.pids = Pids::stats(&self.full_path).map_err(V2ManagerError::PidsStats)?
229 }
230 ControllerType::Memory => stats.memory = Memory::stats(&self.full_path)?,
231 ControllerType::Io => stats.blkio = Io::stats(&self.full_path)?,
232 _ => continue,
233 }
234 }
235
236 Ok(stats)
237 }
238
239 fn get_all_pids(&self) -> Result<Vec<Pid>, Self::Error> {
240 Ok(common::get_all_pids(&self.full_path)?)
241 }
242}