1use crate::signals::{SignalInfo, send_freeze_signal};
11use crate::task::{Kernel, Pid, Task, ThreadGroup, WaitQueue, Waiter};
12use crate::vfs::{FsStr, FsString, PathBuilder};
13use starnix_logging::{CATEGORY_STARNIX, log_warn, track_stub};
14use starnix_sync::{
15 CgroupChildrenLock, CgroupPidTableLock, CgroupStateLock, CgroupV1Level, LockDepGuard,
16 LockDepMutex, allow_subclass,
17};
18use starnix_uapi::errors::Errno;
19use starnix_uapi::signals::SIGKILL;
20use starnix_uapi::{errno, error, pid_t};
21use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet, btree_map, hash_map};
22use std::ops::{Deref, DerefMut};
23use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
24use std::sync::{Arc, Weak};
25
26use crate::signals::KernelSignal;
27use zx;
28
29const CGROUP2_HIERARCHY_ID: u32 = 0;
31
32#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
33pub enum ControllerType {
34 Cpu,
35 Cpuacct,
36 Cpuset,
37 Memory,
38 Freezer,
39 Blkio,
40}
41
42impl ControllerType {
43 pub const ALL: [Self; 6] =
44 [Self::Cpu, Self::Cpuacct, Self::Cpuset, Self::Memory, Self::Freezer, Self::Blkio];
45
46 pub fn as_str(&self) -> &'static str {
47 match self {
48 Self::Cpu => "cpu",
49 Self::Cpuacct => "cpuacct",
50 Self::Cpuset => "cpuset",
51 Self::Memory => "memory",
52 Self::Freezer => "freezer",
53 Self::Blkio => "blkio",
54 }
55 }
56}
57
58impl std::str::FromStr for ControllerType {
59 type Err = ();
60 fn from_str(s: &str) -> Result<Self, Self::Err> {
61 match s {
62 "cpu" => Ok(Self::Cpu),
63 "cpuacct" => Ok(Self::Cpuacct),
64 "cpuset" => Ok(Self::Cpuset),
65 "memory" => Ok(Self::Memory),
66 "freezer" => Ok(Self::Freezer),
67 "blkio" => Ok(Self::Blkio),
68 _ => Err(()),
69 }
70 }
71}
72
73impl std::fmt::Display for ControllerType {
74 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
75 write!(f, "{}", self.as_str())
76 }
77}
78
79#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
80pub struct CgroupV1Key {
81 pub controllers: BTreeSet<ControllerType>,
82 pub name: Option<String>,
83}
84
85#[derive(Default, Debug)]
86pub struct CgroupV1State {
87 pub controllers: HashMap<ControllerType, Arc<CgroupRoot>>,
92 pub named: HashMap<String, Arc<CgroupRoot>>,
94 pub hierarchies: BTreeMap<CgroupV1Key, Arc<CgroupRoot>>,
96 next_hierarchy_id: u32,
98}
99
100#[derive(Debug)]
103pub struct KernelCgroups {
104 pub cgroup2: Arc<CgroupRoot>,
106 pub cgroup1: LockDepMutex<CgroupV1State, CgroupV1Level>,
108}
109
110impl KernelCgroups {
111 pub fn lock_cgroup2_pid_table(&self) -> LockDepGuard<'_, CgroupPidTable> {
118 self.cgroup2.pid_table.lock()
119 }
120
121 pub fn get_or_create_cgroup1(
122 &self,
123 controllers: &BTreeSet<ControllerType>,
124 name: Option<&str>,
125 ) -> Result<Arc<CgroupRoot>, Errno> {
126 let mut cgroup1 = self.cgroup1.lock();
127
128 let key = CgroupV1Key { controllers: controllers.clone(), name: name.map(String::from) };
129
130 if let Some(root) = cgroup1.hierarchies.get(&key) {
131 return Ok(root.clone());
132 }
133
134 for c in controllers {
135 if cgroup1.controllers.contains_key(c) {
136 return error!(EBUSY);
137 }
138 }
139
140 if let Some(n) = name {
141 if cgroup1.named.contains_key(n) {
142 return error!(EBUSY);
143 }
144 }
145
146 cgroup1.next_hierarchy_id += 1;
147 let hierarchy_id = cgroup1.next_hierarchy_id;
148 let root = CgroupRoot::new(hierarchy_id, controllers.clone());
149 cgroup1.hierarchies.insert(key, root.clone());
150 for c in controllers {
151 cgroup1.controllers.insert(*c, root.clone());
152 }
153 if controllers.contains(&ControllerType::Cpuset) {
154 self.cgroup2.v1_cpuset_mounted.store(true, Ordering::Relaxed);
155 }
156 if let Some(n) = name {
157 cgroup1.named.insert(n.to_string(), root.clone());
158 }
159
160 Ok(root)
161 }
162
163 pub fn get_cgroup1(&self, controller: ControllerType, pid: &Pid) -> Option<Weak<Cgroup>> {
164 let cgroup1 = self.cgroup1.lock();
165 let root = cgroup1.controllers.get(&controller)?;
166 root.get_cgroup(pid)
167 }
168}
169
170impl Default for KernelCgroups {
171 fn default() -> Self {
172 Self {
173 cgroup2: CgroupRoot::new(
174 CGROUP2_HIERARCHY_ID,
175 ControllerType::ALL.iter().copied().collect(),
176 ),
177 cgroup1: Default::default(),
178 }
179 }
180}
181
182#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
183pub enum FreezerState {
184 Thawed,
185 Frozen,
186}
187
188impl Default for FreezerState {
189 fn default() -> Self {
190 FreezerState::Thawed
191 }
192}
193
194impl std::fmt::Display for FreezerState {
195 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
196 match self {
197 FreezerState::Frozen => write!(f, "1"),
198 FreezerState::Thawed => write!(f, "0"),
199 }
200 }
201}
202
203#[derive(Default)]
204pub struct CgroupFreezerState {
205 pub self_freezer_state: FreezerState,
207 pub effective_freezer_state: FreezerState,
211}
212
213#[derive(Debug, Default, Clone, PartialEq, Eq)]
214pub struct CpusetControllerState {
215 pub cpus: Option<Vec<u32>>,
216}
217
218#[derive(Debug, Default, Clone, PartialEq, Eq)]
219pub struct FreezerControllerState {
220 pub self_freezer_state: FreezerState,
221 pub inherited_freezer_state: FreezerState,
222}
223
224#[derive(Debug, Clone, PartialEq, Eq)]
225pub enum ControllerState {
226 Cpuset(CpusetControllerState),
227 Freezer(FreezerControllerState),
228}
229
230pub trait FreezerOps: Send + Sync + 'static {
231 fn get_freezer_state(&self) -> CgroupFreezerState;
233
234 fn freeze(&self);
236
237 fn thaw(&self);
239}
240
241pub trait CpusetOps: Send + Sync + 'static {
242 fn cpuset_cpus(&self) -> Vec<u32>;
244
245 fn set_cpuset_cpus(&self, cpus: Vec<u32>);
247}
248
249pub trait CgroupOps: Send + Sync + 'static {
251 fn id(&self) -> u64;
253
254 fn add_process(&self, thread_group: &ThreadGroup) -> Result<(), Errno>;
256
257 fn add_task(&self, task: &Task) -> Result<(), Errno> {
269 self.add_process(task.thread_group())
270 }
271
272 fn new_child(&self, name: &FsStr) -> Result<CgroupHandle, Errno>;
275
276 fn get_children(&self) -> Result<Vec<CgroupHandle>, Errno>;
278
279 fn get_child(&self, name: &FsStr) -> Result<CgroupHandle, Errno>;
281
282 fn remove_child(&self, name: &FsStr) -> Result<CgroupHandle, Errno>;
285
286 fn get_pids(&self, kernel: &Kernel) -> Vec<pid_t>;
288
289 fn kill(&self);
291
292 fn is_populated(&self) -> bool;
294
295 fn freezer(&self) -> Option<&dyn FreezerOps>;
297
298 fn cpuset(&self) -> Option<&dyn CpusetOps>;
300}
301
302#[derive(Debug, Default)]
305pub struct CgroupPidTable(HashMap<Pid, Weak<Cgroup>>);
306impl Deref for CgroupPidTable {
307 type Target = HashMap<Pid, Weak<Cgroup>>;
308
309 fn deref(&self) -> &Self::Target {
310 &self.0
311 }
312}
313impl DerefMut for CgroupPidTable {
314 fn deref_mut(&mut self) -> &mut Self::Target {
315 &mut self.0
316 }
317}
318
319impl CgroupPidTable {
320 pub fn inherit_cgroup(&mut self, parent: &ThreadGroup, child: &ThreadGroup) {
323 assert!(child.read().tasks_count() == 0, "threadgroup must be newly created");
324 if let Some(weak_cgroup) = self.0.get(&parent.leader).cloned() {
325 let Some(cgroup) = weak_cgroup.upgrade() else {
326 log_warn!("ignored attempt to inherit a non-existant cgroup");
327 return;
328 };
329 assert!(
330 self.0.insert(child.leader.clone(), weak_cgroup).is_none(),
331 "child pid should not exist when inheriting"
332 );
333 cgroup.state.lock().processes.insert(child.leader.clone());
335 }
336 }
337
338 pub fn maybe_create_freeze_signal(&self, pid: &Pid) -> Option<KernelSignal> {
340 let Some(weak_cgroup) = self.0.get(pid) else {
341 return None;
342 };
343 let Some(cgroup) = weak_cgroup.upgrade() else {
344 return None;
345 };
346 let state = cgroup.state.lock();
347 if state.get_effective_freezer_state() != FreezerState::Frozen {
348 return None;
349 }
350 Some(KernelSignal::Freeze(state.create_freeze_waiter()))
351 }
352
353 pub fn remove_process(&mut self, pid: &Pid) {
355 if let Some(entry) = self.remove(pid) {
356 if let Some(cgroup) = entry.upgrade() {
357 cgroup.state.lock().processes.remove(pid);
358 }
359 }
360 }
361}
362
363fn set_task_cpuset_path(task: &Task, cpuset_path: &str) -> bool {
365 let mut state = task.write();
366 let changed = state.cpuset_path != cpuset_path;
367 if changed {
368 state.cpuset_path = cpuset_path.to_string();
369 }
370 changed
371}
372
373fn sync_task_scheduler_role(task: &Task) {
374 if let Err(e) = task.sync_scheduler_state_to_role() {
375 log_warn!("Failed to set thread role for task {}: {:?}", task.tid, e);
376 }
377}
378
379#[derive(Debug)]
391pub struct CgroupRoot {
392 pub hierarchy_id: u32,
394
395 pub controllers: BTreeSet<ControllerType>,
397
398 v1_cpuset_mounted: AtomicBool,
400
401 pid_table: LockDepMutex<CgroupPidTable, CgroupPidTableLock>,
403
404 children: LockDepMutex<CgroupChildren, CgroupChildrenLock>,
406
407 weak_self: Weak<CgroupRoot>,
409
410 next_id: AtomicU64,
412}
413
414impl CgroupRoot {
415 pub fn new(hierarchy_id: u32, controllers: BTreeSet<ControllerType>) -> Arc<CgroupRoot> {
416 Arc::new_cyclic(|weak_self| Self {
417 hierarchy_id,
418 controllers,
419 v1_cpuset_mounted: AtomicBool::new(false),
420 pid_table: Default::default(),
421 children: Default::default(),
422 weak_self: weak_self.clone(),
423 next_id: AtomicU64::new(1),
424 })
425 }
426
427 pub fn is_cgroup2(&self) -> bool {
428 self.hierarchy_id == CGROUP2_HIERARCHY_ID
429 }
430
431 pub fn has_controller(&self, controller: ControllerType) -> bool {
432 self.controllers.contains(&controller)
433 }
434
435 fn tracks_cpuset(&self) -> bool {
441 self.has_controller(ControllerType::Cpuset)
442 && !(self.is_cgroup2() && self.v1_cpuset_mounted.load(Ordering::Relaxed))
443 }
444
445 fn remove_process_from_child(
446 &self,
447 thread_group: &ThreadGroup,
448 ) -> Result<LockDepGuard<'_, CgroupPidTable>, Errno> {
449 let mut pid_table = self.pid_table.lock();
450 if let Some(entry) = pid_table.remove(&thread_group.leader) {
454 if let Some(cgroup) = entry.upgrade() {
455 cgroup.state.lock().remove_process(thread_group)?;
456 }
457 }
458 Ok(pid_table)
459 }
460
461 fn get_next_id(&self) -> u64 {
462 self.next_id.fetch_add(1, Ordering::Relaxed)
463 }
464
465 pub fn get_cgroup(&self, pid: &Pid) -> Option<Weak<Cgroup>> {
466 self.pid_table.lock().get(pid).cloned()
467 }
468
469 pub fn get_cgroup_inspect(&self) -> fuchsia_inspect::Inspector {
470 let inspector = fuchsia_inspect::Inspector::default();
471 let cgroups = inspector.root();
472 cgroups.record_uint("pids", self.pid_table.lock().len() as u64);
473 cgroups.record_uint("count", self.children.lock().count_descendants());
474 inspector
475 }
476}
477
478impl CgroupOps for CgroupRoot {
479 fn id(&self) -> u64 {
480 0
481 }
482
483 fn add_process(&self, thread_group: &ThreadGroup) -> Result<(), Errno> {
484 let pid_table = self.remove_process_from_child(thread_group)?;
488
489 if self.tracks_cpuset() {
490 let tasks = thread_group.read().tasks();
491 let tasks_to_sync: Vec<_> =
492 tasks.into_iter().filter(|task| set_task_cpuset_path(task, "/")).collect();
493 drop(pid_table);
494 for task in tasks_to_sync {
495 sync_task_scheduler_role(&task);
496 }
497 }
498
499 Ok(())
500 }
501
502 fn add_task(&self, task: &Task) -> Result<(), Errno> {
503 if self.tracks_cpuset() {
506 let changed = if task.is_leader() {
507 let _pid_table = self.remove_process_from_child(task.thread_group())?;
508 set_task_cpuset_path(task, "/")
509 } else {
510 set_task_cpuset_path(task, "/")
511 };
512 if changed {
513 sync_task_scheduler_role(task);
514 }
515 return Ok(());
516 }
517 self.add_process(task.thread_group())
518 }
519
520 fn new_child(&self, name: &FsStr) -> Result<CgroupHandle, Errno> {
521 let id = self.get_next_id();
522 let new_child = Cgroup::new(id, name, &self.weak_self, None);
523 let mut children = self.children.lock();
524 children.insert_child(name.into(), new_child)
525 }
526
527 fn get_child(&self, name: &FsStr) -> Result<CgroupHandle, Errno> {
528 let children = self.children.lock();
529 children.get_child(name).ok_or_else(|| errno!(ENOENT))
530 }
531
532 fn remove_child(&self, name: &FsStr) -> Result<CgroupHandle, Errno> {
533 let mut children = self.children.lock();
534 children.remove_child(name)
535 }
536
537 fn get_children(&self) -> Result<Vec<CgroupHandle>, Errno> {
538 let children = self.children.lock();
539 Ok(children.get_children())
540 }
541
542 fn get_pids(&self, kernel: &Kernel) -> Vec<pid_t> {
543 let controlled_pids: HashSet<pid_t> = self
544 .pid_table
545 .lock()
546 .keys()
547 .filter_map(|v| v.get_thread_group().ok().map(|tg| tg.leader.id))
548 .collect();
549 let kernel_pids = kernel.pids.process_ids();
550 kernel_pids.into_iter().filter(|pid| !controlled_pids.contains(pid)).collect()
551 }
552
553 fn kill(&self) {
554 unreachable!("Root cgroup cannot kill its processes.");
555 }
556
557 fn is_populated(&self) -> bool {
558 false
559 }
560
561 fn freezer(&self) -> Option<&dyn FreezerOps> {
562 None
563 }
564
565 fn cpuset(&self) -> Option<&dyn CpusetOps> {
566 if self.controllers.contains(&ControllerType::Cpuset) { Some(self) } else { None }
567 }
568}
569
570impl CpusetOps for CgroupRoot {
571 fn cpuset_cpus(&self) -> Vec<u32> {
572 (0..zx::system_get_num_cpus()).collect()
573 }
574
575 fn set_cpuset_cpus(&self, _cpus: Vec<u32>) {}
576}
577
578#[derive(Debug, Default)]
579struct CgroupChildren(BTreeMap<FsString, CgroupHandle>);
580impl CgroupChildren {
581 fn insert_child(&mut self, name: FsString, child: CgroupHandle) -> Result<CgroupHandle, Errno> {
582 let btree_map::Entry::Vacant(child_entry) = self.0.entry(name) else {
583 return error!(EEXIST);
584 };
585 Ok(child_entry.insert(child).clone())
586 }
587
588 fn remove_child(&mut self, name: &FsStr) -> Result<CgroupHandle, Errno> {
589 let btree_map::Entry::Occupied(child_entry) = self.0.entry(name.into()) else {
590 return error!(ENOENT);
591 };
592 let child = child_entry.get();
593
594 let _token = allow_subclass();
598 let mut child_state = child.state.lock();
599 assert!(!child_state.deleted, "child cannot be deleted");
600
601 child_state.update_processes();
602 if !child_state.processes.is_empty() {
603 return error!(EBUSY);
604 }
605 if !child_state.children.is_empty() {
606 return error!(EBUSY);
607 }
608
609 child_state.deleted = true;
610 drop(child_state);
611
612 Ok(child_entry.remove())
613 }
614
615 fn get_child(&self, name: &FsStr) -> Option<CgroupHandle> {
616 self.0.get(name).cloned()
617 }
618
619 fn get_children(&self) -> Vec<CgroupHandle> {
620 self.0.values().cloned().collect()
621 }
622
623 fn count_descendants(&self) -> u64 {
624 self.0
625 .values()
626 .map(|child| {
627 1 + {
628 let _token = allow_subclass();
632 child.count_descendants()
633 }
634 })
635 .sum()
636 }
637}
638
639impl Deref for CgroupChildren {
640 type Target = BTreeMap<FsString, CgroupHandle>;
641
642 fn deref(&self) -> &Self::Target {
643 &self.0
644 }
645}
646
647#[derive(Debug, Default)]
648struct CgroupState {
649 children: CgroupChildren,
651
652 processes: HashSet<Pid>,
654
655 deleted: bool,
657
658 wait_queue: WaitQueue,
660
661 controllers: HashMap<ControllerType, ControllerState>,
663}
664
665impl CgroupState {
666 fn freezer(&self) -> Option<&FreezerControllerState> {
667 match self.controllers.get(&ControllerType::Freezer) {
668 Some(ControllerState::Freezer(state)) => Some(state),
669 _ => None,
670 }
671 }
672
673 fn freezer_mut(&mut self) -> Option<&mut FreezerControllerState> {
674 match self.controllers.get_mut(&ControllerType::Freezer) {
675 Some(ControllerState::Freezer(state)) => Some(state),
676 _ => None,
677 }
678 }
679
680 fn cpuset(&self) -> Option<&CpusetControllerState> {
681 match self.controllers.get(&ControllerType::Cpuset) {
682 Some(ControllerState::Cpuset(state)) => Some(state),
683 _ => None,
684 }
685 }
686
687 fn cpuset_mut(&mut self) -> Option<&mut CpusetControllerState> {
688 match self.controllers.get_mut(&ControllerType::Cpuset) {
689 Some(ControllerState::Cpuset(state)) => Some(state),
690 _ => None,
691 }
692 }
693
694 fn create_freeze_waiter(&self) -> Waiter {
697 let waiter = Waiter::new();
698 self.wait_queue.wait_async(&waiter);
699 waiter
700 }
701
702 fn update_processes(&mut self) {
704 self.processes.retain(|thread_group| {
705 let Ok(thread_group) = thread_group.get_thread_group() else {
706 return false;
707 };
708 let running = thread_group.read().is_running();
709 running
710 });
711 }
712
713 fn freeze_thread_group(&self, thread_group: &ThreadGroup) {
714 let tasks = thread_group.read().tasks();
715 for task in tasks {
716 send_freeze_signal(&task, self.create_freeze_waiter())
717 .expect("sending freeze signal should not fail");
718 }
719 }
720
721 fn thaw_thread_group(&self, thread_group: &ThreadGroup) {
722 let tasks = thread_group.read().tasks();
723 for task in tasks {
724 task.write().thaw();
725 task.interrupt();
726 }
727 }
728
729 fn get_effective_freezer_state(&self) -> FreezerState {
730 if let Some(freezer) = self.freezer() {
731 std::cmp::max(freezer.self_freezer_state, freezer.inherited_freezer_state)
732 } else {
733 FreezerState::Thawed
734 }
735 }
736
737 fn add_process(&mut self, thread_group: &ThreadGroup) -> Result<(), Errno> {
738 if self.deleted {
739 return error!(ENOENT);
740 }
741 self.processes.insert(thread_group.leader.clone());
742
743 if self.get_effective_freezer_state() == FreezerState::Frozen {
744 self.freeze_thread_group(&thread_group);
745 }
746 Ok(())
747 }
748
749 fn remove_process(&mut self, thread_group: &ThreadGroup) -> Result<(), Errno> {
750 if self.deleted {
751 return error!(ENOENT);
752 }
753 self.processes.remove(&thread_group.leader);
754
755 if self.get_effective_freezer_state() == FreezerState::Frozen {
756 self.thaw_thread_group(thread_group);
757 }
758 Ok(())
759 }
760
761 fn propagate_freeze(&mut self, inherited_freezer_state: FreezerState) {
762 let prev_effective_freezer_state = self.get_effective_freezer_state();
763 if let Some(freezer) = self.freezer_mut() {
764 freezer.inherited_freezer_state = inherited_freezer_state;
765 }
766 if prev_effective_freezer_state == FreezerState::Frozen {
767 return;
768 }
769
770 for pid in self.processes.iter() {
771 let Ok(thread_group) = pid.get_thread_group() else {
772 continue;
773 };
774 self.freeze_thread_group(&thread_group);
775 }
776
777 for child in self.children.get_children() {
779 let _token = allow_subclass();
783 child.state.lock().propagate_freeze(FreezerState::Frozen);
784 }
785 }
786
787 fn propagate_thaw(&mut self, inherited_freezer_state: FreezerState) {
788 if let Some(freezer) = self.freezer_mut() {
789 freezer.inherited_freezer_state = inherited_freezer_state;
790 }
791 if self.get_effective_freezer_state() == FreezerState::Thawed {
792 self.wait_queue.notify_all();
793 for child in self.children.get_children() {
794 let _token = allow_subclass();
798 child.state.lock().propagate_thaw(FreezerState::Thawed);
799 }
800 }
801 }
802
803 fn propagate_kill(&self) {
804 for pid in self.processes.iter() {
805 let Ok(thread_group) = pid.get_thread_group() else {
806 continue;
807 };
808 thread_group.write().send_signal(SignalInfo::kernel(SIGKILL));
809 }
810
811 for child in self.children.get_children() {
813 let _token = allow_subclass();
817 child.state.lock().propagate_kill();
818 }
819 }
820}
821
822#[derive(Debug)]
824pub struct Cgroup {
825 root: Weak<CgroupRoot>,
826
827 id: u64,
829
830 name: FsString,
832
833 parent: Option<Weak<Cgroup>>,
836
837 state: LockDepMutex<CgroupState, CgroupStateLock>,
839
840 weak_self: Weak<Cgroup>,
841}
842pub type CgroupHandle = Arc<Cgroup>;
843
844pub fn path_from_root(weak_cgroup: Option<Weak<Cgroup>>) -> Result<FsString, Errno> {
846 let cgroup = match weak_cgroup {
847 Some(weak_cgroup) => Weak::upgrade(&weak_cgroup).ok_or_else(|| errno!(ENODEV))?,
848 None => return Ok("/".into()),
849 };
850 let mut path = PathBuilder::new();
851 let mut current = Some(cgroup);
852 while let Some(cgroup) = current {
853 path.prepend_element(cgroup.name());
854 current = cgroup.parent()?;
855 }
856 Ok(path.build_absolute())
857}
858
859impl Cgroup {
860 pub fn new(
861 id: u64,
862 name: &FsStr,
863 root: &Weak<CgroupRoot>,
864 parent: Option<Weak<Cgroup>>,
865 ) -> CgroupHandle {
866 let root_shared = root.upgrade().expect("root must exist");
867 let mut controllers = HashMap::new();
868 for controller in &root_shared.controllers {
869 match controller {
870 ControllerType::Cpuset => {
871 controllers.insert(
872 *controller,
873 ControllerState::Cpuset(CpusetControllerState::default()),
874 );
875 }
876 ControllerType::Freezer => {
877 controllers.insert(
878 *controller,
879 ControllerState::Freezer(FreezerControllerState::default()),
880 );
881 }
882 _ => {}
883 }
884 }
885
886 Arc::new_cyclic(|weak| Self {
887 id,
888 root: root.clone(),
889 name: name.to_owned(),
890 parent,
891 state: LockDepMutex::new(CgroupState {
892 children: Default::default(),
893 processes: Default::default(),
894 deleted: false,
895 wait_queue: Default::default(),
896 controllers,
897 }),
898 weak_self: weak.clone(),
899 })
900 }
901
902 pub fn name(&self) -> &FsStr {
903 self.name.as_ref()
904 }
905
906 fn root(&self) -> Result<Arc<CgroupRoot>, Errno> {
907 self.root.upgrade().ok_or_else(|| errno!(ENODEV))
908 }
909
910 fn parent(&self) -> Result<Option<CgroupHandle>, Errno> {
913 self.parent.as_ref().map(|weak| weak.upgrade().ok_or_else(|| errno!(ENODEV))).transpose()
914 }
915
916 fn count_descendants(&self) -> u64 {
917 self.state.lock().children.count_descendants()
918 }
919
920 fn is_controller_supported(&self, controller: ControllerType) -> bool {
921 self.root().map(|r| r.controllers.contains(&controller)).unwrap_or(false)
922 }
923
924 fn cpuset_path(&self, root: &CgroupRoot) -> Option<String> {
925 if !root.tracks_cpuset() {
926 return None;
927 }
928 let bytes = path_from_root(Some(self.weak_self.clone())).ok()?;
929 std::str::from_utf8(&bytes).ok().map(|s| s.to_string())
930 }
931
932 fn move_process_into_cgroup<'a>(
933 &self,
934 root: &'a CgroupRoot,
935 thread_group: &ThreadGroup,
936 ) -> Result<LockDepGuard<'a, CgroupPidTable>, Errno> {
937 let mut pid_table = root.pid_table.lock();
938 match pid_table.entry(thread_group.leader.clone()) {
939 hash_map::Entry::Occupied(mut entry) => {
940 if !std::ptr::eq(self, entry.get().as_ptr()) {
943 track_stub!(TODO("https://fxbug.dev/383374687"), "check permissions");
945 if let Some(other_cgroup) = entry.get().upgrade() {
946 other_cgroup.state.lock().remove_process(thread_group)?;
947 }
948
949 self.state.lock().add_process(thread_group)?;
950 entry.insert(self.weak_self.clone());
951 }
952 }
953 hash_map::Entry::Vacant(entry) => {
954 self.state.lock().add_process(thread_group)?;
955 entry.insert(self.weak_self.clone());
956 }
957 }
958 Ok(pid_table)
959 }
960}
961
962impl CgroupOps for Cgroup {
963 fn id(&self) -> u64 {
964 self.id
965 }
966
967 fn add_process(&self, thread_group: &ThreadGroup) -> Result<(), Errno> {
968 let root = self.root()?;
969 let pid_table = self.move_process_into_cgroup(&root, thread_group)?;
973
974 if let Some(cpuset_path) = self.cpuset_path(&root) {
975 let tasks = thread_group.read().tasks();
976 let tasks_to_sync: Vec<_> =
977 tasks.into_iter().filter(|task| set_task_cpuset_path(task, &cpuset_path)).collect();
978 drop(pid_table);
979 for task in tasks_to_sync {
980 sync_task_scheduler_role(&task);
981 }
982 }
983
984 Ok(())
985 }
986
987 fn add_task(&self, task: &Task) -> Result<(), Errno> {
988 let root = self.root()?;
991 if let Some(cpuset_path) = self.cpuset_path(&root) {
992 let changed = if task.is_leader() {
993 let _pid_table = self.move_process_into_cgroup(&root, task.thread_group())?;
994 set_task_cpuset_path(task, &cpuset_path)
995 } else {
996 if self.state.lock().deleted {
997 return error!(ENOENT);
998 }
999 set_task_cpuset_path(task, &cpuset_path)
1000 };
1001 if changed {
1002 sync_task_scheduler_role(task);
1003 }
1004 return Ok(());
1005 }
1006 self.add_process(task.thread_group())
1007 }
1008
1009 fn new_child(&self, name: &FsStr) -> Result<CgroupHandle, Errno> {
1010 let id = self.root()?.get_next_id();
1011 let new_child = Cgroup::new(id, name, &self.root, Some(self.weak_self.clone()));
1012 let mut state = self.state.lock();
1013 if state.deleted {
1014 return error!(ENOENT);
1015 }
1016 let effective_freezer = state.get_effective_freezer_state();
1021 let _token = allow_subclass();
1022 if let Some(freezer) = new_child.state.lock().freezer_mut() {
1023 freezer.inherited_freezer_state = effective_freezer;
1024 }
1025 state.children.insert_child(name.into(), new_child)
1026 }
1027
1028 fn get_child(&self, name: &FsStr) -> Result<CgroupHandle, Errno> {
1029 let state = self.state.lock();
1030 state.children.get_child(name).ok_or_else(|| errno!(ENOENT))
1031 }
1032
1033 fn remove_child(&self, name: &FsStr) -> Result<CgroupHandle, Errno> {
1034 let mut state = self.state.lock();
1035 if state.deleted {
1036 return error!(ENOENT);
1037 }
1038 state.children.remove_child(name)
1039 }
1040
1041 fn get_children(&self) -> Result<Vec<CgroupHandle>, Errno> {
1042 let state = self.state.lock();
1043 if state.deleted {
1044 return error!(ENOENT);
1045 }
1046 Ok(state.children.get_children())
1047 }
1048
1049 fn get_pids(&self, _kernel: &Kernel) -> Vec<pid_t> {
1050 let mut state = self.state.lock();
1051 state.update_processes();
1052 state
1053 .processes
1054 .iter()
1055 .filter_map(|v| v.get_thread_group().ok().map(|tg| tg.leader.id))
1056 .collect()
1057 }
1058
1059 fn kill(&self) {
1060 fuchsia_trace::duration!(CATEGORY_STARNIX, "CgroupKill");
1061 let state = self.state.lock();
1062 state.propagate_kill();
1063 }
1064
1065 fn is_populated(&self) -> bool {
1066 let mut state = self.state.lock();
1067 if state.deleted {
1068 return false;
1069 }
1070 state.update_processes();
1071 if !state.processes.is_empty() {
1072 return true;
1073 }
1074
1075 state.children.get_children().into_iter().any(|child| {
1076 let _token = allow_subclass();
1080 child.is_populated()
1081 })
1082 }
1083
1084 fn freezer(&self) -> Option<&dyn FreezerOps> {
1085 if self.is_controller_supported(ControllerType::Freezer) { Some(self) } else { None }
1086 }
1087
1088 fn cpuset(&self) -> Option<&dyn CpusetOps> {
1089 if self.is_controller_supported(ControllerType::Cpuset) { Some(self) } else { None }
1090 }
1091}
1092
1093impl FreezerOps for Cgroup {
1094 fn get_freezer_state(&self) -> CgroupFreezerState {
1095 let state = self.state.lock();
1096 let self_freezer_state = state.freezer().map(|f| f.self_freezer_state).unwrap_or_default();
1097 CgroupFreezerState {
1098 self_freezer_state,
1099 effective_freezer_state: state.get_effective_freezer_state(),
1100 }
1101 }
1102
1103 fn freeze(&self) {
1104 fuchsia_trace::duration!(CATEGORY_STARNIX, "CgroupFreeze");
1105 let mut state = self.state.lock();
1106 let inherited_freezer_state =
1107 state.freezer().map(|f| f.inherited_freezer_state).unwrap_or_default();
1108 state.propagate_freeze(inherited_freezer_state);
1109 if let Some(freezer) = state.freezer_mut() {
1110 freezer.self_freezer_state = FreezerState::Frozen;
1111 }
1112 }
1113
1114 fn thaw(&self) {
1115 fuchsia_trace::duration!(CATEGORY_STARNIX, "CgroupThaw");
1116 let mut state = self.state.lock();
1117 if let Some(freezer) = state.freezer_mut() {
1118 freezer.self_freezer_state = FreezerState::Thawed;
1119 }
1120 let inherited_freezer_state =
1121 state.freezer().map(|f| f.inherited_freezer_state).unwrap_or_default();
1122 state.propagate_thaw(inherited_freezer_state);
1123 }
1124}
1125
1126impl CpusetOps for Cgroup {
1127 fn cpuset_cpus(&self) -> Vec<u32> {
1128 self.state
1129 .lock()
1130 .cpuset()
1131 .and_then(|c| c.cpus.clone())
1132 .unwrap_or_else(|| (0..zx::system_get_num_cpus()).collect())
1133 }
1134
1135 fn set_cpuset_cpus(&self, cpus: Vec<u32>) {
1136 let mut state = self.state.lock();
1138 if let Some(cpuset) = state.cpuset_mut() {
1139 cpuset.cpus = Some(cpus);
1140 }
1141 }
1142}
1143
1144#[cfg(test)]
1145mod test {
1146 use super::*;
1147 use crate::testing::spawn_kernel_and_run;
1148 use assert_matches::assert_matches;
1149 use starnix_uapi::signals::SIGCHLD;
1150 use starnix_uapi::{CLONE_SIGHAND, CLONE_THREAD, CLONE_VM};
1151
1152 #[::fuchsia::test]
1153 async fn cgroup_path_from_root() {
1154 spawn_kernel_and_run(async |_| {
1155 let root = CgroupRoot::new(0, BTreeSet::new());
1156
1157 let test_cgroup =
1158 root.new_child("test".into()).expect("new_child on root cgroup succeeds");
1159 let child_cgroup = test_cgroup
1160 .new_child("child".into())
1161 .expect("new_child on non-root cgroup succeeds");
1162
1163 assert_eq!(path_from_root(Some(Arc::downgrade(&test_cgroup))), Ok("/test".into()));
1164 assert_eq!(
1165 path_from_root(Some(Arc::downgrade(&child_cgroup))),
1166 Ok("/test/child".into())
1167 );
1168 })
1169 .await;
1170 }
1171
1172 #[::fuchsia::test]
1173 async fn cgroup_clone_task_in_frozen_cgroup() {
1174 spawn_kernel_and_run(async |current_task| {
1175 let kernel = current_task.kernel();
1176 let root = &kernel.cgroups.cgroup2;
1177 let cgroup = root.new_child("test".into()).expect("new_child on root cgroup succeeds");
1178
1179 let process = current_task.clone_task_for_test(0, Some(SIGCHLD));
1180 cgroup.add_process(process.thread_group()).expect("add process to cgroup");
1181 cgroup.freeze();
1182 assert_eq!(cgroup.get_pids(&kernel).first(), Some(process.get_pid()).as_ref());
1183 assert_eq!(root.get_cgroup(&process.pid).unwrap().as_ptr(), Arc::as_ptr(&cgroup));
1184
1185 let thread = process.clone_task_for_test(
1186 (CLONE_THREAD | CLONE_SIGHAND | CLONE_VM) as u64,
1187 Some(SIGCHLD),
1188 );
1189
1190 let thread_state = thread.read();
1191 let kernel_signals = thread_state.kernel_signals_for_test();
1192 assert_matches!(kernel_signals.front(), Some(KernelSignal::Freeze(_)));
1193 })
1194 .await;
1195 }
1196
1197 #[::fuchsia::test]
1198 async fn cgroup_tg_release_removes_pid() {
1199 spawn_kernel_and_run(async |current_task| {
1200 let kernel = current_task.kernel();
1201 let root = &kernel.cgroups.cgroup2;
1202 let cgroup = root.new_child("test".into()).expect("new_child on root cgroup succeeds");
1203
1204 let process = current_task.clone_task_for_test(0, Some(SIGCHLD));
1205 cgroup.add_process(process.thread_group()).expect("add process to cgroup");
1206
1207 assert_eq!(root.get_cgroup(&process.pid).unwrap().as_ptr(), Arc::as_ptr(&cgroup));
1208
1209 drop(process);
1211
1212 assert!(root.pid_table.lock().is_empty());
1214 })
1215 .await;
1216 }
1217
1218 #[::fuchsia::test]
1219 async fn cgroup_add_task_per_thread_cpuset_and_deleted_check() {
1220 spawn_kernel_and_run(async |current_task| {
1221 let kernel = current_task.kernel();
1222 let controllers = BTreeSet::from([ControllerType::Cpuset]);
1223 let v1_root = kernel
1224 .cgroups
1225 .get_or_create_cgroup1(&controllers, None)
1226 .expect("create cgroup1 cpuset");
1227 let top_app = v1_root.new_child("top-app".into()).expect("new_child top-app");
1228 let background = v1_root.new_child("background".into()).expect("new_child background");
1229
1230 let leader = current_task.clone_task_for_test(0, Some(SIGCHLD));
1231 let worker = leader.clone_task_for_test(
1232 (CLONE_THREAD | CLONE_SIGHAND | CLONE_VM) as u64,
1233 Some(SIGCHLD),
1234 );
1235
1236 background.add_task(&worker).expect("add worker to background");
1239 assert_eq!(worker.read().cpuset_path, "/background");
1240 assert_eq!(leader.read().cpuset_path, "/");
1241
1242 top_app.add_task(&leader).expect("add leader to top-app");
1243 assert_eq!(leader.read().cpuset_path, "/top-app");
1244 assert_eq!(worker.read().cpuset_path, "/background");
1245 assert_eq!(v1_root.get_cgroup(&leader.pid).unwrap().as_ptr(), Arc::as_ptr(&top_app));
1246
1247 let v2_child = kernel.cgroups.cgroup2.new_child("uid_1000".into()).expect("v2 child");
1250 v2_child.add_process(leader.thread_group()).expect("add to v2 child");
1251 assert_eq!(leader.read().cpuset_path, "/top-app");
1252 assert_eq!(worker.read().cpuset_path, "/background");
1253
1254 v1_root.remove_child("background".into()).expect("remove background cgroup");
1256 assert_eq!(background.add_task(&worker), error!(ENOENT));
1257 })
1258 .await;
1259 }
1260}