// SPDX-License-Identifier: MPL-2.0 use alloc::{collections::vec_deque::VecDeque, sync::Arc}; use core::{fmt::Display, str::FromStr}; use aster_systree::{Error, Result, SysAttrSetBuilder, SysBranchNode, SysObj}; use bitflags::bitflags; use ostd::{ mm::{VmReader, VmWriter}, sync::{Mutex, MutexGuard, Rcu}, }; use crate::fs::cgroupfs::{ controller::{cpuset::CpuSetController, memory::MemoryController, pids::PidsController}, systree_node::CgroupSysNode, CgroupNode, }; mod cpuset; mod memory; mod pids; /// A trait to abstract all individual cgroup sub-controllers. trait SubControl { fn read_attr_at(&self, name: &str, offset: usize, writer: &mut VmWriter) -> Result; fn write_attr(&self, name: &str, reader: &mut VmReader) -> Result; } /// Defines the static properties and behaviors of a specific cgroup sub-controller. trait SubControlStatic: SubControl + Sized + 'static { /// Creates a new instance of the sub-controller. fn new(is_root: bool) -> Self; /// Returns the `SubCtrlType` enum variant corresponding to this sub-controller. fn type_() -> SubCtrlType; /// Reads and clones the `Arc` of this sub-controller in the given `Controller`. fn read_from(controller: &Controller) -> Arc>; } /// The type of a sub-controller in the cgroup subsystem. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub(super) enum SubCtrlType { Memory, CpuSet, Pids, } impl SubCtrlType { const ALL: [Self; 3] = [Self::Memory, Self::CpuSet, Self::Pids]; } impl FromStr for SubCtrlType { type Err = aster_systree::Error; fn from_str(s: &str) -> Result { match s { "memory" => Ok(SubCtrlType::Memory), "cpuset" => Ok(SubCtrlType::CpuSet), "pids" => Ok(SubCtrlType::Pids), _ => Err(Error::NotFound), } } } bitflags! { /// A set of sub-controller types, represented as bitflags. pub(super) struct SubCtrlSet: u8 { const MEMORY = 1 << 0; const CPUSET = 1 << 1; const PIDS = 1 << 2; } } impl SubCtrlSet { /// Checks whether a sub-control is active in the current set. pub(super) fn contains_type(&self, ctrl_type: SubCtrlType) -> bool { self.contains(ctrl_type.into()) } /// Adds a sub-control type to the current set. pub(super) fn add_type(&mut self, ctrl_type: SubCtrlType) { *self |= ctrl_type.into() } /// Removes a sub-control type from the current set. pub(super) fn remove_type(&mut self, ctrl_type: SubCtrlType) { *self -= ctrl_type.into() } /// Returns an iterator over the sub-controller types in the current set. pub(super) fn iter_types(&self) -> impl Iterator + '_ { SubCtrlType::ALL .into_iter() .filter(|&ctrl_type| self.contains_type(ctrl_type)) } } impl Display for SubCtrlSet { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { if self.contains(Self::MEMORY) { write!(f, "memory ")?; } if self.contains(Self::CPUSET) { write!(f, "cpuset ")?; } if self.contains(Self::PIDS) { write!(f, "pids")?; } Ok(()) } } impl From for SubCtrlSet { fn from(ctrl_type: SubCtrlType) -> Self { match ctrl_type { SubCtrlType::Memory => Self::MEMORY, SubCtrlType::CpuSet => Self::CPUSET, SubCtrlType::Pids => Self::PIDS, } } } /// The sub-controller for a specific cgroup controller type. /// /// If the sub-controller is inactive, the `inner` field will be `None`. struct SubController { inner: Option, /// The parent sub-controller in the hierarchy. /// /// This field is used to traverse the controller hierarchy. #[expect(dead_code)] parent: Option>>, } impl SubController { fn new(parent_controller: Option<&LockedController>) -> Arc { let is_active = if let Some(parent) = parent_controller { parent.active_set.contains_type(T::type_()) } else { true }; let inner = if is_active { Some(T::new(parent_controller.is_none())) } else { None }; let parent = parent_controller.map(|controller| T::read_from(controller.controller)); Arc::new(Self { inner, parent }) } } trait TryGetSubControl { fn try_get(&self) -> Option<&dyn SubControl>; } impl TryGetSubControl for SubController { fn try_get(&self) -> Option<&dyn SubControl> { self.inner.as_ref().map(|sub_ctrl| sub_ctrl as _) } } /// The controller for a single cgroup. /// /// This struct can manage the activation state of each sub-control, and dispatches read/write /// operations to the appropriate sub-controllers. /// /// The following is an explanation of the activation for sub-controls and sub-controllers. When a /// cgroup activates a specific sub-control (e.g., memory, io), it means this control capability is /// being delegated to its children. Consequently, the corresponding sub-controller within the /// child nodes will be activated. /// /// The root node serves as the origin for all these control capabilities, so the sub-controllers /// it possesses are always active. For any other node, only if its parent node first enables a /// sub-control, its corresponding sub-controller will be activated. pub(super) struct Controller { /// A set of types of active sub-controllers. active_set: Mutex, memory: Rcu>>, cpuset: Rcu>>, pids: Rcu>>, } impl Controller { /// Creates a new controller manager for a cgroup. pub(super) fn new(locked_parent_controller: Option<&LockedController>) -> Self { let memory_controller = SubController::new(locked_parent_controller); let cpuset_controller = SubController::new(locked_parent_controller); let pids_controller = SubController::new(locked_parent_controller); Self { active_set: Mutex::new(SubCtrlSet::empty()), memory: Rcu::new(memory_controller), cpuset: Rcu::new(cpuset_controller), pids: Rcu::new(pids_controller), } } pub(super) fn init_attr_set(builder: &mut SysAttrSetBuilder, is_root: bool) { MemoryController::init_attr_set(builder, is_root); CpuSetController::init_attr_set(builder, is_root); PidsController::init_attr_set(builder, is_root); } pub(super) fn lock(&self) -> LockedController { LockedController { active_set: self.active_set.lock(), controller: self, } } fn read_sub(&self, ctrl_type: SubCtrlType) -> Arc { match ctrl_type { SubCtrlType::Memory => MemoryController::read_from(self), SubCtrlType::CpuSet => CpuSetController::read_from(self), SubCtrlType::Pids => PidsController::read_from(self), } } /// Returns whether the attribute with the given name is absent in this controller. pub(super) fn is_attr_absent(&self, name: &str) -> bool { let Some((subsys, _)) = name.split_once('.') else { return false; }; let Ok(ctrl_type) = SubCtrlType::from_str(subsys) else { return false; }; let sub_controller = self.read_sub(ctrl_type); if sub_controller.try_get().is_none() { // If the sub-controller is not active, all its attributes are considered absent. true } else { false } } pub(super) fn read_attr_at( &self, name: &str, offset: usize, writer: &mut VmWriter, ) -> Result { let Some((subsys, _)) = name.split_once('.') else { return Err(Error::NotFound); }; let ctrl_type = SubCtrlType::from_str(subsys)?; let sub_controller = self.read_sub(ctrl_type); let Some(controller) = sub_controller.try_get() else { return Err(Error::IsDead); }; controller.read_attr_at(name, offset, writer) } pub(super) fn write_attr(&self, name: &str, reader: &mut VmReader) -> Result { let Some((subsys, _)) = name.split_once('.') else { return Err(Error::NotFound); }; let ctrl_type = SubCtrlType::from_str(subsys)?; let sub_controller = self.read_sub(ctrl_type); let Some(controller) = sub_controller.try_get() else { return Err(Error::IsDead); }; controller.write_attr(name, reader) } } /// A locked controller for a cgroup. /// /// Holding this lock indicates exclusive access to modify the sub-control state. pub(super) struct LockedController<'a> { active_set: MutexGuard<'a, SubCtrlSet>, controller: &'a Controller, } impl LockedController<'_> { /// Activates a sub-control of the specified type. pub(super) fn activate( &mut self, ctrl_type: SubCtrlType, current_node: &dyn CgroupSysNode, parent_controller: Option<&LockedController>, ) -> Result<()> { if self.active_set.contains_type(ctrl_type) { return Ok(()); } // A cgroup can activate the sub-control only if this // sub-control has been activated in its parent cgroup. if parent_controller .is_some_and(|controller| !controller.active_set.contains_type(ctrl_type)) { return Err(Error::NotFound); } self.active_set.add_type(ctrl_type); self.update_sub_controllers_for_descents(ctrl_type, current_node); Ok(()) } /// Deactivates a sub-control of the specified type. pub(super) fn deactivate( &mut self, ctrl_type: SubCtrlType, current_node: &dyn CgroupSysNode, ) -> Result<()> { if !self.active_set.contains_type(ctrl_type) { return Ok(()); } // If any child node has activated this sub-control, // the deactivation operation will be rejected. for child in current_node.children() { let cgroup_child = child.as_any().downcast_ref::().unwrap(); let child_controller = cgroup_child.controller().lock(); // This is race-free because if a child wants to activate a sub-controller, it should // first acquire the lock of the parent controller, which is held here. if child_controller.active_set().contains_type(ctrl_type) { return Err(Error::InvalidOperation); } } self.active_set.remove_type(ctrl_type); self.update_sub_controllers_for_descents(ctrl_type, current_node); Ok(()) } fn update_sub_controllers_for_descents( &self, ctrl_type: SubCtrlType, current_node: &dyn CgroupSysNode, ) { fn update_sub_controller_for_one_child( child: &Arc, ctrl_type: SubCtrlType, parent_controller: &LockedController, ) { let child_node = child.as_any().downcast_ref::().unwrap(); match ctrl_type { SubCtrlType::Memory => { let new_controller = SubController::new(Some(parent_controller)); child_node.controller().memory.update(new_controller); } SubCtrlType::CpuSet => { let new_controller = SubController::new(Some(parent_controller)); child_node.controller().cpuset.update(new_controller); } SubCtrlType::Pids => { let new_controller = SubController::new(Some(parent_controller)); child_node.controller().pids.update(new_controller); } } } let mut descents = VecDeque::new(); // The following update logic is race-free due to the following reasons: // // 1. **No Concurrent Controller Activation/Deactivation**: // At this point, we hold the controller lock for the current node and we know that the // sub-controllers for the direct children are inactive. Then, no sub-controllers for // any of the descendants can be activated before we release the lock. // // 2. **Concurrent Child Addition/Deletion is Fine**: // We do need to consider that children may be added or removed concurrently. However, // this is handled correctly: // - If a child is added, it will attempt to hold its parent's controller lock, which is // synchronized with the code below. If this happens after us, the up-to-date // sub-controllers will be seen. If it happens before us, we will update the // sub-controllers for it; due to race conditions, the sub-controllers may already be // up to date, but updating them twice is harmless since they must not be activated. // - If a child is removed, we may update a sub-controller that's about to be destroyed, // which is harmless. // Update the direct children first. current_node.visit_children_with(0, &mut |child_node| { descents.push_back(child_node.clone()); update_sub_controller_for_one_child(child_node, ctrl_type, self); Some(()) }); // Then update all the other descendent nodes. while let Some(node) = descents.pop_front() { let current_node = node.as_any().downcast_ref::().unwrap(); // For descendent nodes, the sub-control must be inactive. But taking the controller // lock is necessary for synchronization purposes (see the explanation above). let locked_controller = current_node.controller().lock(); current_node.visit_children_with(0, &mut |child_node| { descents.push_back(child_node.clone()); update_sub_controller_for_one_child(child_node, ctrl_type, &locked_controller); Some(()) }); } } pub(super) fn active_set(&self) -> SubCtrlSet { *self.active_set } }