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, FileOpsCore,
17 LockBefore, LockDepGuard, LockDepMutex, Locked, ThreadGroupLimits, 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;
28
29#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
30pub enum ControllerType {
31 Cpu,
32 Cpuacct,
33 Cpuset,
34 Memory,
35 Freezer,
36 Blkio,
37}
38
39impl ControllerType {
40 pub const ALL: [Self; 6] =
41 [Self::Cpu, Self::Cpuacct, Self::Cpuset, Self::Memory, Self::Freezer, Self::Blkio];
42
43 pub fn as_str(&self) -> &'static str {
44 match self {
45 Self::Cpu => "cpu",
46 Self::Cpuacct => "cpuacct",
47 Self::Cpuset => "cpuset",
48 Self::Memory => "memory",
49 Self::Freezer => "freezer",
50 Self::Blkio => "blkio",
51 }
52 }
53}
54
55impl std::str::FromStr for ControllerType {
56 type Err = ();
57 fn from_str(s: &str) -> Result<Self, Self::Err> {
58 match s {
59 "cpu" => Ok(Self::Cpu),
60 "cpuacct" => Ok(Self::Cpuacct),
61 "cpuset" => Ok(Self::Cpuset),
62 "memory" => Ok(Self::Memory),
63 "freezer" => Ok(Self::Freezer),
64 "blkio" => Ok(Self::Blkio),
65 _ => Err(()),
66 }
67 }
68}
69
70impl std::fmt::Display for ControllerType {
71 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
72 write!(f, "{}", self.as_str())
73 }
74}
75
76#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
77pub struct CgroupV1Key {
78 pub controllers: BTreeSet<ControllerType>,
79 pub name: Option<String>,
80}
81
82#[derive(Default, Debug)]
83pub struct CgroupV1State {
84 pub controllers: HashMap<ControllerType, Arc<CgroupRoot>>,
89 pub named: HashMap<String, Arc<CgroupRoot>>,
91 pub hierarchies: BTreeMap<CgroupV1Key, Arc<CgroupRoot>>,
93 next_hierarchy_id: u32,
95}
96
97#[derive(Debug)]
100pub struct KernelCgroups {
101 pub cgroup2: Arc<CgroupRoot>,
103 pub cgroup1: LockDepMutex<CgroupV1State, CgroupV1Level>,
105}
106
107impl KernelCgroups {
108 pub fn lock_cgroup2_pid_table(&self) -> LockDepGuard<'_, CgroupPidTable> {
115 self.cgroup2.pid_table.lock()
116 }
117
118 pub fn get_or_create_cgroup1(
119 &self,
120 controllers: &BTreeSet<ControllerType>,
121 name: Option<&str>,
122 ) -> Result<Arc<CgroupRoot>, Errno> {
123 let mut cgroup1 = self.cgroup1.lock();
124
125 let key = CgroupV1Key { controllers: controllers.clone(), name: name.map(String::from) };
126
127 if let Some(root) = cgroup1.hierarchies.get(&key) {
128 return Ok(root.clone());
129 }
130
131 for c in controllers {
132 if cgroup1.controllers.contains_key(c) {
133 return error!(EBUSY);
134 }
135 }
136
137 if let Some(n) = name {
138 if cgroup1.named.contains_key(n) {
139 return error!(EBUSY);
140 }
141 }
142
143 cgroup1.next_hierarchy_id += 1;
144 let hierarchy_id = cgroup1.next_hierarchy_id;
145 let root = CgroupRoot::new(hierarchy_id);
146 cgroup1.hierarchies.insert(key, root.clone());
147 for c in controllers {
148 cgroup1.controllers.insert(*c, root.clone());
149 }
150 if let Some(n) = name {
151 cgroup1.named.insert(n.to_string(), root.clone());
152 }
153
154 Ok(root)
155 }
156
157 pub fn get_cgroup1<TG: Copy + Into<ThreadGroupKey>>(
158 &self,
159 controller: ControllerType,
160 tg: TG,
161 ) -> Option<Weak<Cgroup>> {
162 let cgroup1 = self.cgroup1.lock();
163 let root = cgroup1.controllers.get(&controller)?;
164 root.get_cgroup(tg)
165 }
166}
167
168impl Default for KernelCgroups {
169 fn default() -> Self {
170 Self { cgroup2: CgroupRoot::new(0), cgroup1: Default::default() }
171 }
172}
173
174#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
175pub enum FreezerState {
176 Thawed,
177 Frozen,
178}
179
180impl Default for FreezerState {
181 fn default() -> Self {
182 FreezerState::Thawed
183 }
184}
185
186impl std::fmt::Display for FreezerState {
187 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
188 match self {
189 FreezerState::Frozen => write!(f, "1"),
190 FreezerState::Thawed => write!(f, "0"),
191 }
192 }
193}
194
195#[derive(Default)]
196pub struct CgroupFreezerState {
197 pub self_freezer_state: FreezerState,
199 pub effective_freezer_state: FreezerState,
203}
204
205pub trait CgroupOps: Send + Sync + 'static {
207 fn id(&self) -> u64;
209
210 fn add_process(
212 &self,
213 locked: &mut Locked<FileOpsCore>,
214 thread_group: &ThreadGroup,
215 ) -> Result<(), Errno>;
216
217 fn new_child(&self, name: &FsStr) -> Result<CgroupHandle, Errno>;
220
221 fn get_children(&self) -> Result<Vec<CgroupHandle>, Errno>;
223
224 fn get_child(&self, name: &FsStr) -> Result<CgroupHandle, Errno>;
226
227 fn remove_child(&self, name: &FsStr) -> Result<CgroupHandle, Errno>;
230
231 fn get_pids(&self, kernel: &Kernel) -> Vec<pid_t>;
233
234 fn kill(&self);
236
237 fn is_populated(&self) -> bool;
239
240 fn get_freezer_state(&self) -> CgroupFreezerState;
242
243 fn freeze(&self, locked: &mut Locked<FileOpsCore>);
245
246 fn thaw(&self);
248}
249
250#[derive(Debug, Default)]
253pub struct CgroupPidTable(HashMap<ThreadGroupKey, Weak<Cgroup>>);
254impl Deref for CgroupPidTable {
255 type Target = HashMap<ThreadGroupKey, Weak<Cgroup>>;
256
257 fn deref(&self) -> &Self::Target {
258 &self.0
259 }
260}
261impl DerefMut for CgroupPidTable {
262 fn deref_mut(&mut self) -> &mut Self::Target {
263 &mut self.0
264 }
265}
266
267impl CgroupPidTable {
268 pub fn inherit_cgroup(&mut self, parent: &ThreadGroup, child: &ThreadGroup) {
271 assert!(child.read().tasks_count() == 0, "threadgroup must be newly created");
272 if let Some(weak_cgroup) = self.0.get(&parent.into()).cloned() {
273 let Some(cgroup) = weak_cgroup.upgrade() else {
274 log_warn!("ignored attempt to inherit a non-existant cgroup");
275 return;
276 };
277 assert!(
278 self.0.insert(child.into(), weak_cgroup).map(|c| c.strong_count() == 0).is_none(),
279 "child pid should not exist when inheriting"
280 );
281 cgroup.state.lock().processes.insert(child.into());
283 }
284 }
285
286 pub fn maybe_create_freeze_signal<TG: Copy + Into<ThreadGroupKey>>(
288 &self,
289 tg: TG,
290 ) -> Option<KernelSignal> {
291 let Some(weak_cgroup) = self.0.get(&tg.into()) else {
292 return None;
293 };
294 let Some(cgroup) = weak_cgroup.upgrade() else {
295 return None;
296 };
297 let state = cgroup.state.lock();
298 if state.get_effective_freezer_state() != FreezerState::Frozen {
299 return None;
300 }
301 Some(KernelSignal::Freeze(state.create_freeze_waiter()))
302 }
303
304 pub fn remove_process(&mut self, thread_group_key: ThreadGroupKey) {
306 if let Some(entry) = self.remove(&thread_group_key) {
307 if let Some(cgroup) = entry.upgrade() {
308 cgroup.state.lock().processes.remove(&thread_group_key);
309 }
310 }
311 }
312}
313
314#[derive(Debug)]
326pub struct CgroupRoot {
327 pub hierarchy_id: u32,
329
330 pid_table: LockDepMutex<CgroupPidTable, CgroupPidTableLock>,
332
333 children: LockDepMutex<CgroupChildren, CgroupChildrenLock>,
335
336 weak_self: Weak<CgroupRoot>,
338
339 next_id: AtomicU64,
341}
342
343impl CgroupRoot {
344 pub fn new(hierarchy_id: u32) -> Arc<CgroupRoot> {
345 Arc::new_cyclic(|weak_self| Self {
346 hierarchy_id,
347 pid_table: Default::default(),
348 children: Default::default(),
349 weak_self: weak_self.clone(),
350 next_id: AtomicU64::new(1),
351 })
352 }
353
354 fn get_next_id(&self) -> u64 {
355 self.next_id.fetch_add(1, Ordering::Relaxed)
356 }
357
358 pub fn get_cgroup<TG: Copy + Into<ThreadGroupKey>>(&self, tg: TG) -> Option<Weak<Cgroup>> {
359 self.pid_table.lock().get(&tg.into()).cloned()
360 }
361
362 pub fn get_cgroup_inspect(&self) -> fuchsia_inspect::Inspector {
363 let inspector = fuchsia_inspect::Inspector::default();
364 let cgroups = inspector.root();
365 cgroups.record_uint("pids", self.pid_table.lock().len() as u64);
366 cgroups.record_uint("count", self.children.lock().count_descendants());
367 inspector
368 }
369}
370
371impl CgroupOps for CgroupRoot {
372 fn id(&self) -> u64 {
373 0
374 }
375
376 fn add_process(
377 &self,
378 locked: &mut Locked<FileOpsCore>,
379 thread_group: &ThreadGroup,
380 ) -> Result<(), Errno> {
381 let mut pid_table = self.pid_table.lock();
382 if let Some(entry) = pid_table.remove(&thread_group.into()) {
386 if let Some(cgroup) = entry.upgrade() {
387 cgroup.state.lock().remove_process(locked, thread_group)?;
388 }
389 }
390
391 Ok(())
392 }
393
394 fn new_child(&self, name: &FsStr) -> Result<CgroupHandle, Errno> {
395 let id = self.get_next_id();
396 let new_child = Cgroup::new(id, name, &self.weak_self, None);
397 let mut children = self.children.lock();
398 children.insert_child(name.into(), new_child)
399 }
400
401 fn get_child(&self, name: &FsStr) -> Result<CgroupHandle, Errno> {
402 let children = self.children.lock();
403 children.get_child(name).ok_or_else(|| errno!(ENOENT))
404 }
405
406 fn remove_child(&self, name: &FsStr) -> Result<CgroupHandle, Errno> {
407 let mut children = self.children.lock();
408 children.remove_child(name)
409 }
410
411 fn get_children(&self) -> Result<Vec<CgroupHandle>, Errno> {
412 let children = self.children.lock();
413 Ok(children.get_children())
414 }
415
416 fn get_pids(&self, kernel: &Kernel) -> Vec<pid_t> {
417 let controlled_pids: HashSet<pid_t> =
418 self.pid_table.lock().keys().filter_map(|v| v.upgrade().map(|tg| tg.leader)).collect();
419 let kernel_pids = kernel.pids.read().process_ids();
420 kernel_pids.into_iter().filter(|pid| !controlled_pids.contains(pid)).collect()
421 }
422
423 fn kill(&self) {
424 unreachable!("Root cgroup cannot kill its processes.");
425 }
426
427 fn is_populated(&self) -> bool {
428 false
429 }
430
431 fn get_freezer_state(&self) -> CgroupFreezerState {
432 Default::default()
433 }
434
435 fn freeze(&self, _locked: &mut Locked<FileOpsCore>) {
436 unreachable!("Root cgroup cannot freeze any processes.");
437 }
438
439 fn thaw(&self) {
440 unreachable!("Root cgroup cannot thaw any processes.");
441 }
442}
443
444#[derive(Debug, Default)]
445struct CgroupChildren(BTreeMap<FsString, CgroupHandle>);
446impl CgroupChildren {
447 fn insert_child(&mut self, name: FsString, child: CgroupHandle) -> Result<CgroupHandle, Errno> {
448 let btree_map::Entry::Vacant(child_entry) = self.0.entry(name) else {
449 return error!(EEXIST);
450 };
451 Ok(child_entry.insert(child).clone())
452 }
453
454 fn remove_child(&mut self, name: &FsStr) -> Result<CgroupHandle, Errno> {
455 let btree_map::Entry::Occupied(child_entry) = self.0.entry(name.into()) else {
456 return error!(ENOENT);
457 };
458 let child = child_entry.get();
459
460 let _token = allow_subclass();
464 let mut child_state = child.state.lock();
465 assert!(!child_state.deleted, "child cannot be deleted");
466
467 child_state.update_processes();
468 if !child_state.processes.is_empty() {
469 return error!(EBUSY);
470 }
471 if !child_state.children.is_empty() {
472 return error!(EBUSY);
473 }
474
475 child_state.deleted = true;
476 drop(child_state);
477
478 Ok(child_entry.remove())
479 }
480
481 fn get_child(&self, name: &FsStr) -> Option<CgroupHandle> {
482 self.0.get(name).cloned()
483 }
484
485 fn get_children(&self) -> Vec<CgroupHandle> {
486 self.0.values().cloned().collect()
487 }
488
489 fn count_descendants(&self) -> u64 {
490 self.0
491 .values()
492 .map(|child| {
493 1 + {
494 let _token = allow_subclass();
498 child.count_descendants()
499 }
500 })
501 .sum()
502 }
503}
504
505impl Deref for CgroupChildren {
506 type Target = BTreeMap<FsString, CgroupHandle>;
507
508 fn deref(&self) -> &Self::Target {
509 &self.0
510 }
511}
512
513#[derive(Debug, Default)]
514struct CgroupState {
515 children: CgroupChildren,
517
518 processes: HashSet<ThreadGroupKey>,
520
521 deleted: bool,
523
524 wait_queue: WaitQueue,
526
527 self_freezer_state: FreezerState,
529
530 inherited_freezer_state: FreezerState,
532}
533
534impl CgroupState {
535 fn create_freeze_waiter(&self) -> Waiter {
538 let waiter = Waiter::with_options(WaiterOptions::IGNORE_SIGNALS);
539 self.wait_queue.wait_async(&waiter);
540 waiter
541 }
542
543 fn update_processes(&mut self) {
545 self.processes.retain(|thread_group| {
546 let Some(thread_group) = thread_group.upgrade() else {
547 return false;
548 };
549 let running = thread_group.read().is_running();
550 running
551 });
552 }
553
554 fn freeze_thread_group<L>(&self, locked: &mut Locked<L>, thread_group: &ThreadGroup)
555 where
556 L: LockBefore<ThreadGroupLimits>,
557 {
558 let tasks = thread_group.read().tasks();
559 for task in tasks {
560 send_freeze_signal(locked, &task, self.create_freeze_waiter())
561 .expect("sending freeze signal should not fail");
562 }
563 }
564
565 fn thaw_thread_group<L>(&self, _locked: &mut Locked<L>, thread_group: &ThreadGroup)
566 where
567 L: LockBefore<ThreadGroupLimits>,
568 {
569 let tasks = thread_group.read().tasks();
570 for task in tasks {
571 task.write().thaw();
572 task.interrupt();
573 }
574 }
575
576 fn get_effective_freezer_state(&self) -> FreezerState {
577 std::cmp::max(self.self_freezer_state, self.inherited_freezer_state)
578 }
579
580 fn add_process<L>(
581 &mut self,
582 locked: &mut Locked<L>,
583 thread_group: &ThreadGroup,
584 ) -> Result<(), Errno>
585 where
586 L: LockBefore<ThreadGroupLimits>,
587 {
588 if self.deleted {
589 return error!(ENOENT);
590 }
591 self.processes.insert(thread_group.into());
592
593 if self.get_effective_freezer_state() == FreezerState::Frozen {
594 self.freeze_thread_group(locked, &thread_group);
595 }
596 Ok(())
597 }
598
599 fn remove_process<L>(
600 &mut self,
601 locked: &mut Locked<L>,
602 thread_group: &ThreadGroup,
603 ) -> Result<(), Errno>
604 where
605 L: LockBefore<ThreadGroupLimits>,
606 {
607 if self.deleted {
608 return error!(ENOENT);
609 }
610 self.processes.remove(&thread_group.into());
611
612 if self.get_effective_freezer_state() == FreezerState::Frozen {
613 self.thaw_thread_group(locked, thread_group);
614 }
615 Ok(())
616 }
617
618 fn propagate_freeze<L>(&mut self, locked: &mut Locked<L>, inherited_freezer_state: FreezerState)
619 where
620 L: LockBefore<ThreadGroupLimits>,
621 {
622 let prev_effective_freezer_state = self.get_effective_freezer_state();
623 self.inherited_freezer_state = inherited_freezer_state;
624 if prev_effective_freezer_state == FreezerState::Frozen {
625 return;
626 }
627
628 for thread_group in self.processes.iter() {
629 let Some(thread_group) = thread_group.upgrade() else {
630 continue;
631 };
632 self.freeze_thread_group(locked, &thread_group);
633 }
634
635 for child in self.children.get_children() {
637 let _token = allow_subclass();
641 child.state.lock().propagate_freeze(locked, FreezerState::Frozen);
642 }
643 }
644
645 fn propagate_thaw(&mut self, inherited_freezer_state: FreezerState) {
646 self.inherited_freezer_state = inherited_freezer_state;
647 if self.get_effective_freezer_state() == FreezerState::Thawed {
648 self.wait_queue.notify_all();
649 for child in self.children.get_children() {
650 let _token = allow_subclass();
654 child.state.lock().propagate_thaw(FreezerState::Thawed);
655 }
656 }
657 }
658
659 fn propagate_kill(&self) {
660 for thread_group in self.processes.iter() {
661 let Some(thread_group) = thread_group.upgrade() else {
662 continue;
663 };
664 thread_group.write().send_signal(SignalInfo::kernel(SIGKILL));
665 }
666
667 for child in self.children.get_children() {
669 let _token = allow_subclass();
673 child.state.lock().propagate_kill();
674 }
675 }
676}
677
678#[derive(Debug)]
680pub struct Cgroup {
681 root: Weak<CgroupRoot>,
682
683 id: u64,
685
686 name: FsString,
688
689 parent: Option<Weak<Cgroup>>,
692
693 state: LockDepMutex<CgroupState, CgroupStateLock>,
695
696 weak_self: Weak<Cgroup>,
697}
698pub type CgroupHandle = Arc<Cgroup>;
699
700pub fn path_from_root(weak_cgroup: Option<Weak<Cgroup>>) -> Result<FsString, Errno> {
702 let cgroup = match weak_cgroup {
703 Some(weak_cgroup) => Weak::upgrade(&weak_cgroup).ok_or_else(|| errno!(ENODEV))?,
704 None => return Ok("/".into()),
705 };
706 let mut path = PathBuilder::new();
707 let mut current = Some(cgroup);
708 while let Some(cgroup) = current {
709 path.prepend_element(cgroup.name());
710 current = cgroup.parent()?;
711 }
712 Ok(path.build_absolute())
713}
714
715impl Cgroup {
716 pub fn new(
717 id: u64,
718 name: &FsStr,
719 root: &Weak<CgroupRoot>,
720 parent: Option<Weak<Cgroup>>,
721 ) -> CgroupHandle {
722 Arc::new_cyclic(|weak| Self {
723 id,
724 root: root.clone(),
725 name: name.to_owned(),
726 parent,
727 state: Default::default(),
728 weak_self: weak.clone(),
729 })
730 }
731
732 pub fn name(&self) -> &FsStr {
733 self.name.as_ref()
734 }
735
736 fn root(&self) -> Result<Arc<CgroupRoot>, Errno> {
737 self.root.upgrade().ok_or_else(|| errno!(ENODEV))
738 }
739
740 fn parent(&self) -> Result<Option<CgroupHandle>, Errno> {
743 self.parent.as_ref().map(|weak| weak.upgrade().ok_or_else(|| errno!(ENODEV))).transpose()
744 }
745
746 fn count_descendants(&self) -> u64 {
747 self.state.lock().children.count_descendants()
748 }
749}
750
751impl CgroupOps for Cgroup {
752 fn id(&self) -> u64 {
753 self.id
754 }
755
756 fn add_process(
757 &self,
758 locked: &mut Locked<FileOpsCore>,
759 thread_group: &ThreadGroup,
760 ) -> Result<(), Errno> {
761 let root = self.root()?;
762 let mut pid_table = root.pid_table.lock();
763 match pid_table.entry(thread_group.into()) {
764 hash_map::Entry::Occupied(mut entry) => {
765 if std::ptr::eq(self, entry.get().as_ptr()) {
768 return Ok(());
769 }
770
771 track_stub!(TODO("https://fxbug.dev/383374687"), "check permissions");
773 if let Some(other_cgroup) = entry.get().upgrade() {
774 other_cgroup.state.lock().remove_process(locked, thread_group)?;
775 }
776
777 self.state.lock().add_process(locked, thread_group)?;
778 entry.insert(self.weak_self.clone());
779 }
780 hash_map::Entry::Vacant(entry) => {
781 self.state.lock().add_process(locked, thread_group)?;
782 entry.insert(self.weak_self.clone());
783 }
784 }
785
786 Ok(())
787 }
788
789 fn new_child(&self, name: &FsStr) -> Result<CgroupHandle, Errno> {
790 let id = self.root()?.get_next_id();
791 let new_child = Cgroup::new(id, name, &self.root, Some(self.weak_self.clone()));
792 let mut state = self.state.lock();
793 if state.deleted {
794 return error!(ENOENT);
795 }
796 let _token = allow_subclass();
801 new_child.state.lock().inherited_freezer_state = state.get_effective_freezer_state();
802 state.children.insert_child(name.into(), new_child)
803 }
804
805 fn get_child(&self, name: &FsStr) -> Result<CgroupHandle, Errno> {
806 let state = self.state.lock();
807 state.children.get_child(name).ok_or_else(|| errno!(ENOENT))
808 }
809
810 fn remove_child(&self, name: &FsStr) -> Result<CgroupHandle, Errno> {
811 let mut state = self.state.lock();
812 if state.deleted {
813 return error!(ENOENT);
814 }
815 state.children.remove_child(name)
816 }
817
818 fn get_children(&self) -> Result<Vec<CgroupHandle>, Errno> {
819 let state = self.state.lock();
820 if state.deleted {
821 return error!(ENOENT);
822 }
823 Ok(state.children.get_children())
824 }
825
826 fn get_pids(&self, _kernel: &Kernel) -> Vec<pid_t> {
827 let mut state = self.state.lock();
828 state.update_processes();
829 state.processes.iter().filter_map(|v| v.upgrade().map(|tg| tg.leader)).collect()
830 }
831
832 fn kill(&self) {
833 fuchsia_trace::duration!(CATEGORY_STARNIX, "CgroupKill");
834 let state = self.state.lock();
835 state.propagate_kill();
836 }
837
838 fn is_populated(&self) -> bool {
839 let mut state = self.state.lock();
840 if state.deleted {
841 return false;
842 }
843 state.update_processes();
844 if !state.processes.is_empty() {
845 return true;
846 }
847
848 state.children.get_children().into_iter().any(|child| {
849 let _token = allow_subclass();
853 child.is_populated()
854 })
855 }
856
857 fn get_freezer_state(&self) -> CgroupFreezerState {
858 let state = self.state.lock();
859 CgroupFreezerState {
860 self_freezer_state: state.self_freezer_state,
861 effective_freezer_state: state.get_effective_freezer_state(),
862 }
863 }
864
865 fn freeze(&self, locked: &mut Locked<FileOpsCore>) {
866 fuchsia_trace::duration!(CATEGORY_STARNIX, "CgroupFreeze");
867 let mut state = self.state.lock();
868 let inherited_freezer_state = state.inherited_freezer_state;
869 state.propagate_freeze(locked, inherited_freezer_state);
870 state.self_freezer_state = FreezerState::Frozen;
871 }
872
873 fn thaw(&self) {
874 fuchsia_trace::duration!(CATEGORY_STARNIX, "CgroupThaw");
875 let mut state = self.state.lock();
876 state.self_freezer_state = FreezerState::Thawed;
877 let inherited_freezer_state = state.inherited_freezer_state;
878 state.propagate_thaw(inherited_freezer_state);
879 }
880}
881
882#[cfg(test)]
883mod test {
884 use super::*;
885 use crate::testing::spawn_kernel_and_run;
886 use assert_matches::assert_matches;
887 use starnix_uapi::signals::SIGCHLD;
888 use starnix_uapi::{CLONE_SIGHAND, CLONE_THREAD, CLONE_VM};
889
890 #[::fuchsia::test]
891 async fn cgroup_path_from_root() {
892 spawn_kernel_and_run(async |_, _| {
893 let root = CgroupRoot::new(0);
894
895 let test_cgroup =
896 root.new_child("test".into()).expect("new_child on root cgroup succeeds");
897 let child_cgroup = test_cgroup
898 .new_child("child".into())
899 .expect("new_child on non-root cgroup succeeds");
900
901 assert_eq!(path_from_root(Some(Arc::downgrade(&test_cgroup))), Ok("/test".into()));
902 assert_eq!(
903 path_from_root(Some(Arc::downgrade(&child_cgroup))),
904 Ok("/test/child".into())
905 );
906 })
907 .await;
908 }
909
910 #[::fuchsia::test]
911 async fn cgroup_clone_task_in_frozen_cgroup() {
912 spawn_kernel_and_run(async |locked, current_task| {
913 let kernel = current_task.kernel();
914 let root = &kernel.cgroups.cgroup2;
915 let cgroup = root.new_child("test".into()).expect("new_child on root cgroup succeeds");
916
917 let process = current_task.clone_task_for_test(locked, 0, Some(SIGCHLD));
918 cgroup
919 .add_process(locked.cast_locked(), process.thread_group())
920 .expect("add process to cgroup");
921 cgroup.freeze(locked.cast_locked());
922 assert_eq!(cgroup.get_pids(&kernel).first(), Some(process.get_pid()).as_ref());
923 assert_eq!(
924 root.get_cgroup(process.thread_group()).unwrap().as_ptr(),
925 Arc::as_ptr(&cgroup)
926 );
927
928 let thread = process.clone_task_for_test(
929 locked,
930 (CLONE_THREAD | CLONE_SIGHAND | CLONE_VM) as u64,
931 Some(SIGCHLD),
932 );
933
934 let thread_state = thread.read();
935 let kernel_signals = thread_state.kernel_signals_for_test();
936 assert_matches!(kernel_signals.front(), Some(KernelSignal::Freeze(_)));
937 })
938 .await;
939 }
940
941 #[::fuchsia::test]
942 async fn cgroup_tg_release_removes_pid() {
943 spawn_kernel_and_run(async |locked, current_task| {
944 let kernel = current_task.kernel();
945 let root = &kernel.cgroups.cgroup2;
946 let cgroup = root.new_child("test".into()).expect("new_child on root cgroup succeeds");
947
948 let process = current_task.clone_task_for_test(locked, 0, Some(SIGCHLD));
949 cgroup
950 .add_process(locked.cast_locked(), process.thread_group())
951 .expect("add process to cgroup");
952
953 assert_eq!(
954 root.get_cgroup(process.thread_group()).unwrap().as_ptr(),
955 Arc::as_ptr(&cgroup)
956 );
957
958 drop(process);
960
961 assert!(root.pid_table.lock().is_empty());
963 })
964 .await;
965 }
966}