1use crate::signals::{SignalInfo, send_freeze_signal};
11use crate::task::waiter::WaiterOptions;
12use crate::task::{Kernel, ThreadGroup, ThreadGroupKey, WaitQueue, Waiter};
13use crate::vfs::{FsStr, FsString, PathBuilder};
14use starnix_logging::{CATEGORY_STARNIX, log_warn, track_stub};
15use starnix_sync::{
16 CgroupChildrenLock, CgroupPidTableLock, CgroupStateLock, CgroupV1Level, LockDepGuard,
17 LockDepMutex, allow_subclass,
18};
19use starnix_uapi::errors::Errno;
20use starnix_uapi::signals::SIGKILL;
21use starnix_uapi::{errno, error, pid_t};
22use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet, btree_map, hash_map};
23use std::ops::{Deref, DerefMut};
24use std::sync::atomic::{AtomicU64, Ordering};
25use std::sync::{Arc, Weak};
26
27use crate::signals::KernelSignal;
28use zx;
29
30#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
31pub enum ControllerType {
32 Cpu,
33 Cpuacct,
34 Cpuset,
35 Memory,
36 Freezer,
37 Blkio,
38}
39
40impl ControllerType {
41 pub const ALL: [Self; 6] =
42 [Self::Cpu, Self::Cpuacct, Self::Cpuset, Self::Memory, Self::Freezer, Self::Blkio];
43
44 pub fn as_str(&self) -> &'static str {
45 match self {
46 Self::Cpu => "cpu",
47 Self::Cpuacct => "cpuacct",
48 Self::Cpuset => "cpuset",
49 Self::Memory => "memory",
50 Self::Freezer => "freezer",
51 Self::Blkio => "blkio",
52 }
53 }
54}
55
56impl std::str::FromStr for ControllerType {
57 type Err = ();
58 fn from_str(s: &str) -> Result<Self, Self::Err> {
59 match s {
60 "cpu" => Ok(Self::Cpu),
61 "cpuacct" => Ok(Self::Cpuacct),
62 "cpuset" => Ok(Self::Cpuset),
63 "memory" => Ok(Self::Memory),
64 "freezer" => Ok(Self::Freezer),
65 "blkio" => Ok(Self::Blkio),
66 _ => Err(()),
67 }
68 }
69}
70
71impl std::fmt::Display for ControllerType {
72 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
73 write!(f, "{}", self.as_str())
74 }
75}
76
77#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
78pub struct CgroupV1Key {
79 pub controllers: BTreeSet<ControllerType>,
80 pub name: Option<String>,
81}
82
83#[derive(Default, Debug)]
84pub struct CgroupV1State {
85 pub controllers: HashMap<ControllerType, Arc<CgroupRoot>>,
90 pub named: HashMap<String, Arc<CgroupRoot>>,
92 pub hierarchies: BTreeMap<CgroupV1Key, Arc<CgroupRoot>>,
94 next_hierarchy_id: u32,
96}
97
98#[derive(Debug)]
101pub struct KernelCgroups {
102 pub cgroup2: Arc<CgroupRoot>,
104 pub cgroup1: LockDepMutex<CgroupV1State, CgroupV1Level>,
106}
107
108impl KernelCgroups {
109 pub fn lock_cgroup2_pid_table(&self) -> LockDepGuard<'_, CgroupPidTable> {
116 self.cgroup2.pid_table.lock()
117 }
118
119 pub fn get_or_create_cgroup1(
120 &self,
121 controllers: &BTreeSet<ControllerType>,
122 name: Option<&str>,
123 ) -> Result<Arc<CgroupRoot>, Errno> {
124 let mut cgroup1 = self.cgroup1.lock();
125
126 let key = CgroupV1Key { controllers: controllers.clone(), name: name.map(String::from) };
127
128 if let Some(root) = cgroup1.hierarchies.get(&key) {
129 return Ok(root.clone());
130 }
131
132 for c in controllers {
133 if cgroup1.controllers.contains_key(c) {
134 return error!(EBUSY);
135 }
136 }
137
138 if let Some(n) = name {
139 if cgroup1.named.contains_key(n) {
140 return error!(EBUSY);
141 }
142 }
143
144 cgroup1.next_hierarchy_id += 1;
145 let hierarchy_id = cgroup1.next_hierarchy_id;
146 let root = CgroupRoot::new(hierarchy_id, controllers.clone());
147 cgroup1.hierarchies.insert(key, root.clone());
148 for c in controllers {
149 cgroup1.controllers.insert(*c, root.clone());
150 }
151 if let Some(n) = name {
152 cgroup1.named.insert(n.to_string(), root.clone());
153 }
154
155 Ok(root)
156 }
157
158 pub fn get_cgroup1<TG: Copy + Into<ThreadGroupKey>>(
159 &self,
160 controller: ControllerType,
161 tg: TG,
162 ) -> Option<Weak<Cgroup>> {
163 let cgroup1 = self.cgroup1.lock();
164 let root = cgroup1.controllers.get(&controller)?;
165 root.get_cgroup(tg)
166 }
167}
168
169impl Default for KernelCgroups {
170 fn default() -> Self {
171 Self {
172 cgroup2: CgroupRoot::new(0, ControllerType::ALL.iter().copied().collect()),
173 cgroup1: Default::default(),
174 }
175 }
176}
177
178#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
179pub enum FreezerState {
180 Thawed,
181 Frozen,
182}
183
184impl Default for FreezerState {
185 fn default() -> Self {
186 FreezerState::Thawed
187 }
188}
189
190impl std::fmt::Display for FreezerState {
191 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
192 match self {
193 FreezerState::Frozen => write!(f, "1"),
194 FreezerState::Thawed => write!(f, "0"),
195 }
196 }
197}
198
199#[derive(Default)]
200pub struct CgroupFreezerState {
201 pub self_freezer_state: FreezerState,
203 pub effective_freezer_state: FreezerState,
207}
208
209#[derive(Debug, Default, Clone, PartialEq, Eq)]
210pub struct CpusetControllerState {
211 pub cpus: Option<Vec<u32>>,
212}
213
214#[derive(Debug, Default, Clone, PartialEq, Eq)]
215pub struct FreezerControllerState {
216 pub self_freezer_state: FreezerState,
217 pub inherited_freezer_state: FreezerState,
218}
219
220#[derive(Debug, Clone, PartialEq, Eq)]
221pub enum ControllerState {
222 Cpuset(CpusetControllerState),
223 Freezer(FreezerControllerState),
224}
225
226pub trait FreezerOps: Send + Sync + 'static {
227 fn get_freezer_state(&self) -> CgroupFreezerState;
229
230 fn freeze(&self);
232
233 fn thaw(&self);
235}
236
237pub trait CpusetOps: Send + Sync + 'static {
238 fn cpuset_cpus(&self) -> Vec<u32>;
240
241 fn set_cpuset_cpus(&self, cpus: Vec<u32>);
243}
244
245pub trait CgroupOps: Send + Sync + 'static {
247 fn id(&self) -> u64;
249
250 fn add_process(&self, thread_group: &ThreadGroup) -> Result<(), Errno>;
252
253 fn new_child(&self, name: &FsStr) -> Result<CgroupHandle, Errno>;
256
257 fn get_children(&self) -> Result<Vec<CgroupHandle>, Errno>;
259
260 fn get_child(&self, name: &FsStr) -> Result<CgroupHandle, Errno>;
262
263 fn remove_child(&self, name: &FsStr) -> Result<CgroupHandle, Errno>;
266
267 fn get_pids(&self, kernel: &Kernel) -> Vec<pid_t>;
269
270 fn kill(&self);
272
273 fn is_populated(&self) -> bool;
275
276 fn freezer(&self) -> Option<&dyn FreezerOps>;
278
279 fn cpuset(&self) -> Option<&dyn CpusetOps>;
281}
282
283#[derive(Debug, Default)]
286pub struct CgroupPidTable(HashMap<ThreadGroupKey, Weak<Cgroup>>);
287impl Deref for CgroupPidTable {
288 type Target = HashMap<ThreadGroupKey, Weak<Cgroup>>;
289
290 fn deref(&self) -> &Self::Target {
291 &self.0
292 }
293}
294impl DerefMut for CgroupPidTable {
295 fn deref_mut(&mut self) -> &mut Self::Target {
296 &mut self.0
297 }
298}
299
300impl CgroupPidTable {
301 pub fn inherit_cgroup(&mut self, parent: &ThreadGroup, child: &ThreadGroup) {
304 assert!(child.read().tasks_count() == 0, "threadgroup must be newly created");
305 if let Some(weak_cgroup) = self.0.get(&parent.into()).cloned() {
306 let Some(cgroup) = weak_cgroup.upgrade() else {
307 log_warn!("ignored attempt to inherit a non-existant cgroup");
308 return;
309 };
310 assert!(
311 self.0.insert(child.into(), weak_cgroup).map(|c| c.strong_count() == 0).is_none(),
312 "child pid should not exist when inheriting"
313 );
314 cgroup.state.lock().processes.insert(child.into());
316 }
317 }
318
319 pub fn maybe_create_freeze_signal<TG: Copy + Into<ThreadGroupKey>>(
321 &self,
322 tg: TG,
323 ) -> Option<KernelSignal> {
324 let Some(weak_cgroup) = self.0.get(&tg.into()) else {
325 return None;
326 };
327 let Some(cgroup) = weak_cgroup.upgrade() else {
328 return None;
329 };
330 let state = cgroup.state.lock();
331 if state.get_effective_freezer_state() != FreezerState::Frozen {
332 return None;
333 }
334 Some(KernelSignal::Freeze(state.create_freeze_waiter()))
335 }
336
337 pub fn remove_process(&mut self, thread_group_key: ThreadGroupKey) {
339 if let Some(entry) = self.remove(&thread_group_key) {
340 if let Some(cgroup) = entry.upgrade() {
341 cgroup.state.lock().processes.remove(&thread_group_key);
342 }
343 }
344 }
345}
346
347#[derive(Debug)]
359pub struct CgroupRoot {
360 pub hierarchy_id: u32,
362
363 pub controllers: BTreeSet<ControllerType>,
365
366 pid_table: LockDepMutex<CgroupPidTable, CgroupPidTableLock>,
368
369 children: LockDepMutex<CgroupChildren, CgroupChildrenLock>,
371
372 weak_self: Weak<CgroupRoot>,
374
375 next_id: AtomicU64,
377}
378
379impl CgroupRoot {
380 pub fn new(hierarchy_id: u32, controllers: BTreeSet<ControllerType>) -> Arc<CgroupRoot> {
381 Arc::new_cyclic(|weak_self| Self {
382 hierarchy_id,
383 controllers,
384 pid_table: Default::default(),
385 children: Default::default(),
386 weak_self: weak_self.clone(),
387 next_id: AtomicU64::new(1),
388 })
389 }
390
391 pub fn has_controller(&self, controller: ControllerType) -> bool {
392 self.controllers.contains(&controller)
393 }
394
395 fn get_next_id(&self) -> u64 {
396 self.next_id.fetch_add(1, Ordering::Relaxed)
397 }
398
399 pub fn get_cgroup<TG: Copy + Into<ThreadGroupKey>>(&self, tg: TG) -> Option<Weak<Cgroup>> {
400 self.pid_table.lock().get(&tg.into()).cloned()
401 }
402
403 pub fn get_cgroup_inspect(&self) -> fuchsia_inspect::Inspector {
404 let inspector = fuchsia_inspect::Inspector::default();
405 let cgroups = inspector.root();
406 cgroups.record_uint("pids", self.pid_table.lock().len() as u64);
407 cgroups.record_uint("count", self.children.lock().count_descendants());
408 inspector
409 }
410}
411
412impl CgroupOps for CgroupRoot {
413 fn id(&self) -> u64 {
414 0
415 }
416
417 fn add_process(&self, thread_group: &ThreadGroup) -> Result<(), Errno> {
418 let mut pid_table = self.pid_table.lock();
419 if let Some(entry) = pid_table.remove(&thread_group.into()) {
423 if let Some(cgroup) = entry.upgrade() {
424 cgroup.state.lock().remove_process(thread_group)?;
425 }
426 }
427
428 let tasks = thread_group.read().tasks();
429 if self.has_controller(ControllerType::Cpuset) {
430 for task in &tasks {
431 task.write().cpuset_path = "/".to_string();
432 }
433 }
434
435 for task in tasks {
437 if let Err(e) = task.sync_scheduler_state_to_role() {
438 log_warn!("Failed to set thread role for task {}: {:?}", task.tid, e);
439 }
440 }
441
442 Ok(())
443 }
444
445 fn new_child(&self, name: &FsStr) -> Result<CgroupHandle, Errno> {
446 let id = self.get_next_id();
447 let new_child = Cgroup::new(id, name, &self.weak_self, None);
448 let mut children = self.children.lock();
449 children.insert_child(name.into(), new_child)
450 }
451
452 fn get_child(&self, name: &FsStr) -> Result<CgroupHandle, Errno> {
453 let children = self.children.lock();
454 children.get_child(name).ok_or_else(|| errno!(ENOENT))
455 }
456
457 fn remove_child(&self, name: &FsStr) -> Result<CgroupHandle, Errno> {
458 let mut children = self.children.lock();
459 children.remove_child(name)
460 }
461
462 fn get_children(&self) -> Result<Vec<CgroupHandle>, Errno> {
463 let children = self.children.lock();
464 Ok(children.get_children())
465 }
466
467 fn get_pids(&self, kernel: &Kernel) -> Vec<pid_t> {
468 let controlled_pids: HashSet<pid_t> =
469 self.pid_table.lock().keys().filter_map(|v| v.upgrade().map(|tg| tg.leader)).collect();
470 let kernel_pids = kernel.pids.read().process_ids();
471 kernel_pids.into_iter().filter(|pid| !controlled_pids.contains(pid)).collect()
472 }
473
474 fn kill(&self) {
475 unreachable!("Root cgroup cannot kill its processes.");
476 }
477
478 fn is_populated(&self) -> bool {
479 false
480 }
481
482 fn freezer(&self) -> Option<&dyn FreezerOps> {
483 None
484 }
485
486 fn cpuset(&self) -> Option<&dyn CpusetOps> {
487 if self.controllers.contains(&ControllerType::Cpuset) { Some(self) } else { None }
488 }
489}
490
491impl CpusetOps for CgroupRoot {
492 fn cpuset_cpus(&self) -> Vec<u32> {
493 (0..zx::system_get_num_cpus()).collect()
494 }
495
496 fn set_cpuset_cpus(&self, _cpus: Vec<u32>) {}
497}
498
499#[derive(Debug, Default)]
500struct CgroupChildren(BTreeMap<FsString, CgroupHandle>);
501impl CgroupChildren {
502 fn insert_child(&mut self, name: FsString, child: CgroupHandle) -> Result<CgroupHandle, Errno> {
503 let btree_map::Entry::Vacant(child_entry) = self.0.entry(name) else {
504 return error!(EEXIST);
505 };
506 Ok(child_entry.insert(child).clone())
507 }
508
509 fn remove_child(&mut self, name: &FsStr) -> Result<CgroupHandle, Errno> {
510 let btree_map::Entry::Occupied(child_entry) = self.0.entry(name.into()) else {
511 return error!(ENOENT);
512 };
513 let child = child_entry.get();
514
515 let _token = allow_subclass();
519 let mut child_state = child.state.lock();
520 assert!(!child_state.deleted, "child cannot be deleted");
521
522 child_state.update_processes();
523 if !child_state.processes.is_empty() {
524 return error!(EBUSY);
525 }
526 if !child_state.children.is_empty() {
527 return error!(EBUSY);
528 }
529
530 child_state.deleted = true;
531 drop(child_state);
532
533 Ok(child_entry.remove())
534 }
535
536 fn get_child(&self, name: &FsStr) -> Option<CgroupHandle> {
537 self.0.get(name).cloned()
538 }
539
540 fn get_children(&self) -> Vec<CgroupHandle> {
541 self.0.values().cloned().collect()
542 }
543
544 fn count_descendants(&self) -> u64 {
545 self.0
546 .values()
547 .map(|child| {
548 1 + {
549 let _token = allow_subclass();
553 child.count_descendants()
554 }
555 })
556 .sum()
557 }
558}
559
560impl Deref for CgroupChildren {
561 type Target = BTreeMap<FsString, CgroupHandle>;
562
563 fn deref(&self) -> &Self::Target {
564 &self.0
565 }
566}
567
568#[derive(Debug, Default)]
569struct CgroupState {
570 children: CgroupChildren,
572
573 processes: HashSet<ThreadGroupKey>,
575
576 deleted: bool,
578
579 wait_queue: WaitQueue,
581
582 controllers: HashMap<ControllerType, ControllerState>,
584}
585
586impl CgroupState {
587 fn freezer(&self) -> Option<&FreezerControllerState> {
588 match self.controllers.get(&ControllerType::Freezer) {
589 Some(ControllerState::Freezer(state)) => Some(state),
590 _ => None,
591 }
592 }
593
594 fn freezer_mut(&mut self) -> Option<&mut FreezerControllerState> {
595 match self.controllers.get_mut(&ControllerType::Freezer) {
596 Some(ControllerState::Freezer(state)) => Some(state),
597 _ => None,
598 }
599 }
600
601 fn cpuset(&self) -> Option<&CpusetControllerState> {
602 match self.controllers.get(&ControllerType::Cpuset) {
603 Some(ControllerState::Cpuset(state)) => Some(state),
604 _ => None,
605 }
606 }
607
608 fn cpuset_mut(&mut self) -> Option<&mut CpusetControllerState> {
609 match self.controllers.get_mut(&ControllerType::Cpuset) {
610 Some(ControllerState::Cpuset(state)) => Some(state),
611 _ => None,
612 }
613 }
614
615 fn create_freeze_waiter(&self) -> Waiter {
618 let waiter = Waiter::with_options(WaiterOptions::IGNORE_SIGNALS);
619 self.wait_queue.wait_async(&waiter);
620 waiter
621 }
622
623 fn update_processes(&mut self) {
625 self.processes.retain(|thread_group| {
626 let Some(thread_group) = thread_group.upgrade() else {
627 return false;
628 };
629 let running = thread_group.read().is_running();
630 running
631 });
632 }
633
634 fn freeze_thread_group(&self, thread_group: &ThreadGroup) {
635 let tasks = thread_group.read().tasks();
636 for task in tasks {
637 send_freeze_signal(&task, self.create_freeze_waiter())
638 .expect("sending freeze signal should not fail");
639 }
640 }
641
642 fn thaw_thread_group(&self, thread_group: &ThreadGroup) {
643 let tasks = thread_group.read().tasks();
644 for task in tasks {
645 task.write().thaw();
646 task.interrupt();
647 }
648 }
649
650 fn get_effective_freezer_state(&self) -> FreezerState {
651 if let Some(freezer) = self.freezer() {
652 std::cmp::max(freezer.self_freezer_state, freezer.inherited_freezer_state)
653 } else {
654 FreezerState::Thawed
655 }
656 }
657
658 fn add_process(&mut self, thread_group: &ThreadGroup) -> Result<(), Errno> {
659 if self.deleted {
660 return error!(ENOENT);
661 }
662 self.processes.insert(thread_group.into());
663
664 if self.get_effective_freezer_state() == FreezerState::Frozen {
665 self.freeze_thread_group(&thread_group);
666 }
667 Ok(())
668 }
669
670 fn remove_process(&mut self, thread_group: &ThreadGroup) -> Result<(), Errno> {
671 if self.deleted {
672 return error!(ENOENT);
673 }
674 self.processes.remove(&thread_group.into());
675
676 if self.get_effective_freezer_state() == FreezerState::Frozen {
677 self.thaw_thread_group(thread_group);
678 }
679 Ok(())
680 }
681
682 fn propagate_freeze(&mut self, inherited_freezer_state: FreezerState) {
683 let prev_effective_freezer_state = self.get_effective_freezer_state();
684 if let Some(freezer) = self.freezer_mut() {
685 freezer.inherited_freezer_state = inherited_freezer_state;
686 }
687 if prev_effective_freezer_state == FreezerState::Frozen {
688 return;
689 }
690
691 for thread_group in self.processes.iter() {
692 let Some(thread_group) = thread_group.upgrade() else {
693 continue;
694 };
695 self.freeze_thread_group(&thread_group);
696 }
697
698 for child in self.children.get_children() {
700 let _token = allow_subclass();
704 child.state.lock().propagate_freeze(FreezerState::Frozen);
705 }
706 }
707
708 fn propagate_thaw(&mut self, inherited_freezer_state: FreezerState) {
709 if let Some(freezer) = self.freezer_mut() {
710 freezer.inherited_freezer_state = inherited_freezer_state;
711 }
712 if self.get_effective_freezer_state() == FreezerState::Thawed {
713 self.wait_queue.notify_all();
714 for child in self.children.get_children() {
715 let _token = allow_subclass();
719 child.state.lock().propagate_thaw(FreezerState::Thawed);
720 }
721 }
722 }
723
724 fn propagate_kill(&self) {
725 for thread_group in self.processes.iter() {
726 let Some(thread_group) = thread_group.upgrade() else {
727 continue;
728 };
729 thread_group.write().send_signal(SignalInfo::kernel(SIGKILL));
730 }
731
732 for child in self.children.get_children() {
734 let _token = allow_subclass();
738 child.state.lock().propagate_kill();
739 }
740 }
741}
742
743#[derive(Debug)]
745pub struct Cgroup {
746 root: Weak<CgroupRoot>,
747
748 id: u64,
750
751 name: FsString,
753
754 parent: Option<Weak<Cgroup>>,
757
758 state: LockDepMutex<CgroupState, CgroupStateLock>,
760
761 weak_self: Weak<Cgroup>,
762}
763pub type CgroupHandle = Arc<Cgroup>;
764
765pub fn path_from_root(weak_cgroup: Option<Weak<Cgroup>>) -> Result<FsString, Errno> {
767 let cgroup = match weak_cgroup {
768 Some(weak_cgroup) => Weak::upgrade(&weak_cgroup).ok_or_else(|| errno!(ENODEV))?,
769 None => return Ok("/".into()),
770 };
771 let mut path = PathBuilder::new();
772 let mut current = Some(cgroup);
773 while let Some(cgroup) = current {
774 path.prepend_element(cgroup.name());
775 current = cgroup.parent()?;
776 }
777 Ok(path.build_absolute())
778}
779
780impl Cgroup {
781 pub fn new(
782 id: u64,
783 name: &FsStr,
784 root: &Weak<CgroupRoot>,
785 parent: Option<Weak<Cgroup>>,
786 ) -> CgroupHandle {
787 let root_shared = root.upgrade().expect("root must exist");
788 let mut controllers = HashMap::new();
789 for controller in &root_shared.controllers {
790 match controller {
791 ControllerType::Cpuset => {
792 controllers.insert(
793 *controller,
794 ControllerState::Cpuset(CpusetControllerState::default()),
795 );
796 }
797 ControllerType::Freezer => {
798 controllers.insert(
799 *controller,
800 ControllerState::Freezer(FreezerControllerState::default()),
801 );
802 }
803 _ => {}
804 }
805 }
806
807 Arc::new_cyclic(|weak| Self {
808 id,
809 root: root.clone(),
810 name: name.to_owned(),
811 parent,
812 state: LockDepMutex::new(CgroupState {
813 children: Default::default(),
814 processes: Default::default(),
815 deleted: false,
816 wait_queue: Default::default(),
817 controllers,
818 }),
819 weak_self: weak.clone(),
820 })
821 }
822
823 pub fn name(&self) -> &FsStr {
824 self.name.as_ref()
825 }
826
827 fn root(&self) -> Result<Arc<CgroupRoot>, Errno> {
828 self.root.upgrade().ok_or_else(|| errno!(ENODEV))
829 }
830
831 fn parent(&self) -> Result<Option<CgroupHandle>, Errno> {
834 self.parent.as_ref().map(|weak| weak.upgrade().ok_or_else(|| errno!(ENODEV))).transpose()
835 }
836
837 fn count_descendants(&self) -> u64 {
838 self.state.lock().children.count_descendants()
839 }
840
841 fn is_controller_supported(&self, controller: ControllerType) -> bool {
842 self.root().map(|r| r.controllers.contains(&controller)).unwrap_or(false)
843 }
844
845 fn cpuset_path(&self, root: &CgroupRoot) -> Option<String> {
846 if !root.has_controller(ControllerType::Cpuset) {
847 return None;
848 }
849 let bytes = path_from_root(Some(self.weak_self.clone())).ok()?;
850 std::str::from_utf8(&bytes).ok().map(|s| s.to_string())
851 }
852}
853
854impl CgroupOps for Cgroup {
855 fn id(&self) -> u64 {
856 self.id
857 }
858
859 fn add_process(&self, thread_group: &ThreadGroup) -> Result<(), Errno> {
860 let root = self.root()?;
861 let mut pid_table = root.pid_table.lock();
862 match pid_table.entry(thread_group.into()) {
863 hash_map::Entry::Occupied(mut entry) => {
864 if std::ptr::eq(self, entry.get().as_ptr()) {
867 return Ok(());
868 }
869
870 track_stub!(TODO("https://fxbug.dev/383374687"), "check permissions");
872 if let Some(other_cgroup) = entry.get().upgrade() {
873 other_cgroup.state.lock().remove_process(thread_group)?;
874 }
875
876 self.state.lock().add_process(thread_group)?;
877 entry.insert(self.weak_self.clone());
878 }
879 hash_map::Entry::Vacant(entry) => {
880 self.state.lock().add_process(thread_group)?;
881 entry.insert(self.weak_self.clone());
882 }
883 }
884
885 let tasks = thread_group.read().tasks();
886 if let Some(cpuset_path) = self.cpuset_path(&root) {
887 for task in &tasks {
888 task.write().cpuset_path = cpuset_path.clone();
889 }
890 }
891
892 for task in tasks {
894 if let Err(e) = task.sync_scheduler_state_to_role() {
895 log_warn!("Failed to set thread role for task {}: {:?}", task.tid, e);
896 }
897 }
898
899 Ok(())
900 }
901
902 fn new_child(&self, name: &FsStr) -> Result<CgroupHandle, Errno> {
903 let id = self.root()?.get_next_id();
904 let new_child = Cgroup::new(id, name, &self.root, Some(self.weak_self.clone()));
905 let mut state = self.state.lock();
906 if state.deleted {
907 return error!(ENOENT);
908 }
909 let effective_freezer = state.get_effective_freezer_state();
914 let _token = allow_subclass();
915 if let Some(freezer) = new_child.state.lock().freezer_mut() {
916 freezer.inherited_freezer_state = effective_freezer;
917 }
918 state.children.insert_child(name.into(), new_child)
919 }
920
921 fn get_child(&self, name: &FsStr) -> Result<CgroupHandle, Errno> {
922 let state = self.state.lock();
923 state.children.get_child(name).ok_or_else(|| errno!(ENOENT))
924 }
925
926 fn remove_child(&self, name: &FsStr) -> Result<CgroupHandle, Errno> {
927 let mut state = self.state.lock();
928 if state.deleted {
929 return error!(ENOENT);
930 }
931 state.children.remove_child(name)
932 }
933
934 fn get_children(&self) -> Result<Vec<CgroupHandle>, Errno> {
935 let state = self.state.lock();
936 if state.deleted {
937 return error!(ENOENT);
938 }
939 Ok(state.children.get_children())
940 }
941
942 fn get_pids(&self, _kernel: &Kernel) -> Vec<pid_t> {
943 let mut state = self.state.lock();
944 state.update_processes();
945 state.processes.iter().filter_map(|v| v.upgrade().map(|tg| tg.leader)).collect()
946 }
947
948 fn kill(&self) {
949 fuchsia_trace::duration!(CATEGORY_STARNIX, "CgroupKill");
950 let state = self.state.lock();
951 state.propagate_kill();
952 }
953
954 fn is_populated(&self) -> bool {
955 let mut state = self.state.lock();
956 if state.deleted {
957 return false;
958 }
959 state.update_processes();
960 if !state.processes.is_empty() {
961 return true;
962 }
963
964 state.children.get_children().into_iter().any(|child| {
965 let _token = allow_subclass();
969 child.is_populated()
970 })
971 }
972
973 fn freezer(&self) -> Option<&dyn FreezerOps> {
974 if self.is_controller_supported(ControllerType::Freezer) { Some(self) } else { None }
975 }
976
977 fn cpuset(&self) -> Option<&dyn CpusetOps> {
978 if self.is_controller_supported(ControllerType::Cpuset) { Some(self) } else { None }
979 }
980}
981
982impl FreezerOps for Cgroup {
983 fn get_freezer_state(&self) -> CgroupFreezerState {
984 let state = self.state.lock();
985 let self_freezer_state = state.freezer().map(|f| f.self_freezer_state).unwrap_or_default();
986 CgroupFreezerState {
987 self_freezer_state,
988 effective_freezer_state: state.get_effective_freezer_state(),
989 }
990 }
991
992 fn freeze(&self) {
993 fuchsia_trace::duration!(CATEGORY_STARNIX, "CgroupFreeze");
994 let mut state = self.state.lock();
995 let inherited_freezer_state =
996 state.freezer().map(|f| f.inherited_freezer_state).unwrap_or_default();
997 state.propagate_freeze(inherited_freezer_state);
998 if let Some(freezer) = state.freezer_mut() {
999 freezer.self_freezer_state = FreezerState::Frozen;
1000 }
1001 }
1002
1003 fn thaw(&self) {
1004 fuchsia_trace::duration!(CATEGORY_STARNIX, "CgroupThaw");
1005 let mut state = self.state.lock();
1006 if let Some(freezer) = state.freezer_mut() {
1007 freezer.self_freezer_state = FreezerState::Thawed;
1008 }
1009 let inherited_freezer_state =
1010 state.freezer().map(|f| f.inherited_freezer_state).unwrap_or_default();
1011 state.propagate_thaw(inherited_freezer_state);
1012 }
1013}
1014
1015impl CpusetOps for Cgroup {
1016 fn cpuset_cpus(&self) -> Vec<u32> {
1017 self.state
1018 .lock()
1019 .cpuset()
1020 .and_then(|c| c.cpus.clone())
1021 .unwrap_or_else(|| (0..zx::system_get_num_cpus()).collect())
1022 }
1023
1024 fn set_cpuset_cpus(&self, cpus: Vec<u32>) {
1025 let mut state = self.state.lock();
1027 if let Some(cpuset) = state.cpuset_mut() {
1028 cpuset.cpus = Some(cpus);
1029 }
1030 }
1031}
1032
1033#[cfg(test)]
1034mod test {
1035 use super::*;
1036 use crate::testing::spawn_kernel_and_run;
1037 use assert_matches::assert_matches;
1038 use starnix_uapi::signals::SIGCHLD;
1039 use starnix_uapi::{CLONE_SIGHAND, CLONE_THREAD, CLONE_VM};
1040
1041 #[::fuchsia::test]
1042 async fn cgroup_path_from_root() {
1043 spawn_kernel_and_run(async |_| {
1044 let root = CgroupRoot::new(0, BTreeSet::new());
1045
1046 let test_cgroup =
1047 root.new_child("test".into()).expect("new_child on root cgroup succeeds");
1048 let child_cgroup = test_cgroup
1049 .new_child("child".into())
1050 .expect("new_child on non-root cgroup succeeds");
1051
1052 assert_eq!(path_from_root(Some(Arc::downgrade(&test_cgroup))), Ok("/test".into()));
1053 assert_eq!(
1054 path_from_root(Some(Arc::downgrade(&child_cgroup))),
1055 Ok("/test/child".into())
1056 );
1057 })
1058 .await;
1059 }
1060
1061 #[::fuchsia::test]
1062 async fn cgroup_clone_task_in_frozen_cgroup() {
1063 spawn_kernel_and_run(async |current_task| {
1064 let kernel = current_task.kernel();
1065 let root = &kernel.cgroups.cgroup2;
1066 let cgroup = root.new_child("test".into()).expect("new_child on root cgroup succeeds");
1067
1068 let process = current_task.clone_task_for_test(0, Some(SIGCHLD));
1069 cgroup.add_process(process.thread_group()).expect("add process to cgroup");
1070 cgroup.freeze();
1071 assert_eq!(cgroup.get_pids(&kernel).first(), Some(process.get_pid()).as_ref());
1072 assert_eq!(
1073 root.get_cgroup(process.thread_group()).unwrap().as_ptr(),
1074 Arc::as_ptr(&cgroup)
1075 );
1076
1077 let thread = process.clone_task_for_test(
1078 (CLONE_THREAD | CLONE_SIGHAND | CLONE_VM) as u64,
1079 Some(SIGCHLD),
1080 );
1081
1082 let thread_state = thread.read();
1083 let kernel_signals = thread_state.kernel_signals_for_test();
1084 assert_matches!(kernel_signals.front(), Some(KernelSignal::Freeze(_)));
1085 })
1086 .await;
1087 }
1088
1089 #[::fuchsia::test]
1090 async fn cgroup_tg_release_removes_pid() {
1091 spawn_kernel_and_run(async |current_task| {
1092 let kernel = current_task.kernel();
1093 let root = &kernel.cgroups.cgroup2;
1094 let cgroup = root.new_child("test".into()).expect("new_child on root cgroup succeeds");
1095
1096 let process = current_task.clone_task_for_test(0, Some(SIGCHLD));
1097 cgroup.add_process(process.thread_group()).expect("add process to cgroup");
1098
1099 assert_eq!(
1100 root.get_cgroup(process.thread_group()).unwrap().as_ptr(),
1101 Arc::as_ptr(&cgroup)
1102 );
1103
1104 drop(process);
1106
1107 assert!(root.pid_table.lock().is_empty());
1109 })
1110 .await;
1111 }
1112}