1use crate::signals::{SignalInfo, send_freeze_signal};
11use crate::task::waiter::WaiterOptions;
12use crate::task::{Kernel, Pid, ThreadGroup, 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(&self, controller: ControllerType, pid: &Pid) -> Option<Weak<Cgroup>> {
159 let cgroup1 = self.cgroup1.lock();
160 let root = cgroup1.controllers.get(&controller)?;
161 root.get_cgroup(pid)
162 }
163}
164
165impl Default for KernelCgroups {
166 fn default() -> Self {
167 Self {
168 cgroup2: CgroupRoot::new(0, ControllerType::ALL.iter().copied().collect()),
169 cgroup1: Default::default(),
170 }
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
205#[derive(Debug, Default, Clone, PartialEq, Eq)]
206pub struct CpusetControllerState {
207 pub cpus: Option<Vec<u32>>,
208}
209
210#[derive(Debug, Default, Clone, PartialEq, Eq)]
211pub struct FreezerControllerState {
212 pub self_freezer_state: FreezerState,
213 pub inherited_freezer_state: FreezerState,
214}
215
216#[derive(Debug, Clone, PartialEq, Eq)]
217pub enum ControllerState {
218 Cpuset(CpusetControllerState),
219 Freezer(FreezerControllerState),
220}
221
222pub trait FreezerOps: Send + Sync + 'static {
223 fn get_freezer_state(&self) -> CgroupFreezerState;
225
226 fn freeze(&self);
228
229 fn thaw(&self);
231}
232
233pub trait CpusetOps: Send + Sync + 'static {
234 fn cpuset_cpus(&self) -> Vec<u32>;
236
237 fn set_cpuset_cpus(&self, cpus: Vec<u32>);
239}
240
241pub trait CgroupOps: Send + Sync + 'static {
243 fn id(&self) -> u64;
245
246 fn add_process(&self, thread_group: &ThreadGroup) -> Result<(), Errno>;
248
249 fn new_child(&self, name: &FsStr) -> Result<CgroupHandle, Errno>;
252
253 fn get_children(&self) -> Result<Vec<CgroupHandle>, Errno>;
255
256 fn get_child(&self, name: &FsStr) -> Result<CgroupHandle, Errno>;
258
259 fn remove_child(&self, name: &FsStr) -> Result<CgroupHandle, Errno>;
262
263 fn get_pids(&self, kernel: &Kernel) -> Vec<pid_t>;
265
266 fn kill(&self);
268
269 fn is_populated(&self) -> bool;
271
272 fn freezer(&self) -> Option<&dyn FreezerOps>;
274
275 fn cpuset(&self) -> Option<&dyn CpusetOps>;
277}
278
279#[derive(Debug, Default)]
282pub struct CgroupPidTable(HashMap<Pid, Weak<Cgroup>>);
283impl Deref for CgroupPidTable {
284 type Target = HashMap<Pid, Weak<Cgroup>>;
285
286 fn deref(&self) -> &Self::Target {
287 &self.0
288 }
289}
290impl DerefMut for CgroupPidTable {
291 fn deref_mut(&mut self) -> &mut Self::Target {
292 &mut self.0
293 }
294}
295
296impl CgroupPidTable {
297 pub fn inherit_cgroup(&mut self, parent: &ThreadGroup, child: &ThreadGroup) {
300 assert!(child.read().tasks_count() == 0, "threadgroup must be newly created");
301 if let Some(weak_cgroup) = self.0.get(&parent.leader).cloned() {
302 let Some(cgroup) = weak_cgroup.upgrade() else {
303 log_warn!("ignored attempt to inherit a non-existant cgroup");
304 return;
305 };
306 assert!(
307 self.0.insert(child.leader.clone(), weak_cgroup).is_none(),
308 "child pid should not exist when inheriting"
309 );
310 cgroup.state.lock().processes.insert(child.leader.clone());
312 }
313 }
314
315 pub fn maybe_create_freeze_signal(&self, pid: &Pid) -> Option<KernelSignal> {
317 let Some(weak_cgroup) = self.0.get(pid) else {
318 return None;
319 };
320 let Some(cgroup) = weak_cgroup.upgrade() else {
321 return None;
322 };
323 let state = cgroup.state.lock();
324 if state.get_effective_freezer_state() != FreezerState::Frozen {
325 return None;
326 }
327 Some(KernelSignal::Freeze(state.create_freeze_waiter()))
328 }
329
330 pub fn remove_process(&mut self, pid: &Pid) {
332 if let Some(entry) = self.remove(pid) {
333 if let Some(cgroup) = entry.upgrade() {
334 cgroup.state.lock().processes.remove(pid);
335 }
336 }
337 }
338}
339
340#[derive(Debug)]
352pub struct CgroupRoot {
353 pub hierarchy_id: u32,
355
356 pub controllers: BTreeSet<ControllerType>,
358
359 pid_table: LockDepMutex<CgroupPidTable, CgroupPidTableLock>,
361
362 children: LockDepMutex<CgroupChildren, CgroupChildrenLock>,
364
365 weak_self: Weak<CgroupRoot>,
367
368 next_id: AtomicU64,
370}
371
372impl CgroupRoot {
373 pub fn new(hierarchy_id: u32, controllers: BTreeSet<ControllerType>) -> Arc<CgroupRoot> {
374 Arc::new_cyclic(|weak_self| Self {
375 hierarchy_id,
376 controllers,
377 pid_table: Default::default(),
378 children: Default::default(),
379 weak_self: weak_self.clone(),
380 next_id: AtomicU64::new(1),
381 })
382 }
383
384 pub fn has_controller(&self, controller: ControllerType) -> bool {
385 self.controllers.contains(&controller)
386 }
387
388 fn get_next_id(&self) -> u64 {
389 self.next_id.fetch_add(1, Ordering::Relaxed)
390 }
391
392 pub fn get_cgroup(&self, pid: &Pid) -> Option<Weak<Cgroup>> {
393 self.pid_table.lock().get(pid).cloned()
394 }
395
396 pub fn get_cgroup_inspect(&self) -> fuchsia_inspect::Inspector {
397 let inspector = fuchsia_inspect::Inspector::default();
398 let cgroups = inspector.root();
399 cgroups.record_uint("pids", self.pid_table.lock().len() as u64);
400 cgroups.record_uint("count", self.children.lock().count_descendants());
401 inspector
402 }
403}
404
405impl CgroupOps for CgroupRoot {
406 fn id(&self) -> u64 {
407 0
408 }
409
410 fn add_process(&self, thread_group: &ThreadGroup) -> Result<(), Errno> {
411 let mut pid_table = self.pid_table.lock();
412 if let Some(entry) = pid_table.remove(&thread_group.leader) {
416 if let Some(cgroup) = entry.upgrade() {
417 cgroup.state.lock().remove_process(thread_group)?;
418 }
419 }
420
421 let tasks = thread_group.read().tasks();
422 if self.has_controller(ControllerType::Cpuset) {
423 for task in &tasks {
424 task.write().cpuset_path = "/".to_string();
425 }
426 }
427
428 for task in tasks {
430 if let Err(e) = task.sync_scheduler_state_to_role() {
431 log_warn!("Failed to set thread role for task {}: {:?}", task.tid, e);
432 }
433 }
434
435 Ok(())
436 }
437
438 fn new_child(&self, name: &FsStr) -> Result<CgroupHandle, Errno> {
439 let id = self.get_next_id();
440 let new_child = Cgroup::new(id, name, &self.weak_self, None);
441 let mut children = self.children.lock();
442 children.insert_child(name.into(), new_child)
443 }
444
445 fn get_child(&self, name: &FsStr) -> Result<CgroupHandle, Errno> {
446 let children = self.children.lock();
447 children.get_child(name).ok_or_else(|| errno!(ENOENT))
448 }
449
450 fn remove_child(&self, name: &FsStr) -> Result<CgroupHandle, Errno> {
451 let mut children = self.children.lock();
452 children.remove_child(name)
453 }
454
455 fn get_children(&self) -> Result<Vec<CgroupHandle>, Errno> {
456 let children = self.children.lock();
457 Ok(children.get_children())
458 }
459
460 fn get_pids(&self, kernel: &Kernel) -> Vec<pid_t> {
461 let controlled_pids: HashSet<pid_t> = self
462 .pid_table
463 .lock()
464 .keys()
465 .filter_map(|v| v.get_thread_group().ok().map(|tg| tg.leader.id))
466 .collect();
467 let kernel_pids = kernel.pids.process_ids();
468 kernel_pids.into_iter().filter(|pid| !controlled_pids.contains(pid)).collect()
469 }
470
471 fn kill(&self) {
472 unreachable!("Root cgroup cannot kill its processes.");
473 }
474
475 fn is_populated(&self) -> bool {
476 false
477 }
478
479 fn freezer(&self) -> Option<&dyn FreezerOps> {
480 None
481 }
482
483 fn cpuset(&self) -> Option<&dyn CpusetOps> {
484 if self.controllers.contains(&ControllerType::Cpuset) { Some(self) } else { None }
485 }
486}
487
488impl CpusetOps for CgroupRoot {
489 fn cpuset_cpus(&self) -> Vec<u32> {
490 (0..zx::system_get_num_cpus()).collect()
491 }
492
493 fn set_cpuset_cpus(&self, _cpus: Vec<u32>) {}
494}
495
496#[derive(Debug, Default)]
497struct CgroupChildren(BTreeMap<FsString, CgroupHandle>);
498impl CgroupChildren {
499 fn insert_child(&mut self, name: FsString, child: CgroupHandle) -> Result<CgroupHandle, Errno> {
500 let btree_map::Entry::Vacant(child_entry) = self.0.entry(name) else {
501 return error!(EEXIST);
502 };
503 Ok(child_entry.insert(child).clone())
504 }
505
506 fn remove_child(&mut self, name: &FsStr) -> Result<CgroupHandle, Errno> {
507 let btree_map::Entry::Occupied(child_entry) = self.0.entry(name.into()) else {
508 return error!(ENOENT);
509 };
510 let child = child_entry.get();
511
512 let _token = allow_subclass();
516 let mut child_state = child.state.lock();
517 assert!(!child_state.deleted, "child cannot be deleted");
518
519 child_state.update_processes();
520 if !child_state.processes.is_empty() {
521 return error!(EBUSY);
522 }
523 if !child_state.children.is_empty() {
524 return error!(EBUSY);
525 }
526
527 child_state.deleted = true;
528 drop(child_state);
529
530 Ok(child_entry.remove())
531 }
532
533 fn get_child(&self, name: &FsStr) -> Option<CgroupHandle> {
534 self.0.get(name).cloned()
535 }
536
537 fn get_children(&self) -> Vec<CgroupHandle> {
538 self.0.values().cloned().collect()
539 }
540
541 fn count_descendants(&self) -> u64 {
542 self.0
543 .values()
544 .map(|child| {
545 1 + {
546 let _token = allow_subclass();
550 child.count_descendants()
551 }
552 })
553 .sum()
554 }
555}
556
557impl Deref for CgroupChildren {
558 type Target = BTreeMap<FsString, CgroupHandle>;
559
560 fn deref(&self) -> &Self::Target {
561 &self.0
562 }
563}
564
565#[derive(Debug, Default)]
566struct CgroupState {
567 children: CgroupChildren,
569
570 processes: HashSet<Pid>,
572
573 deleted: bool,
575
576 wait_queue: WaitQueue,
578
579 controllers: HashMap<ControllerType, ControllerState>,
581}
582
583impl CgroupState {
584 fn freezer(&self) -> Option<&FreezerControllerState> {
585 match self.controllers.get(&ControllerType::Freezer) {
586 Some(ControllerState::Freezer(state)) => Some(state),
587 _ => None,
588 }
589 }
590
591 fn freezer_mut(&mut self) -> Option<&mut FreezerControllerState> {
592 match self.controllers.get_mut(&ControllerType::Freezer) {
593 Some(ControllerState::Freezer(state)) => Some(state),
594 _ => None,
595 }
596 }
597
598 fn cpuset(&self) -> Option<&CpusetControllerState> {
599 match self.controllers.get(&ControllerType::Cpuset) {
600 Some(ControllerState::Cpuset(state)) => Some(state),
601 _ => None,
602 }
603 }
604
605 fn cpuset_mut(&mut self) -> Option<&mut CpusetControllerState> {
606 match self.controllers.get_mut(&ControllerType::Cpuset) {
607 Some(ControllerState::Cpuset(state)) => Some(state),
608 _ => None,
609 }
610 }
611
612 fn create_freeze_waiter(&self) -> Waiter {
615 let waiter = Waiter::with_options(WaiterOptions::IGNORE_SIGNALS);
616 self.wait_queue.wait_async(&waiter);
617 waiter
618 }
619
620 fn update_processes(&mut self) {
622 self.processes.retain(|thread_group| {
623 let Ok(thread_group) = thread_group.get_thread_group() else {
624 return false;
625 };
626 let running = thread_group.read().is_running();
627 running
628 });
629 }
630
631 fn freeze_thread_group(&self, thread_group: &ThreadGroup) {
632 let tasks = thread_group.read().tasks();
633 for task in tasks {
634 send_freeze_signal(&task, self.create_freeze_waiter())
635 .expect("sending freeze signal should not fail");
636 }
637 }
638
639 fn thaw_thread_group(&self, thread_group: &ThreadGroup) {
640 let tasks = thread_group.read().tasks();
641 for task in tasks {
642 task.write().thaw();
643 task.interrupt();
644 }
645 }
646
647 fn get_effective_freezer_state(&self) -> FreezerState {
648 if let Some(freezer) = self.freezer() {
649 std::cmp::max(freezer.self_freezer_state, freezer.inherited_freezer_state)
650 } else {
651 FreezerState::Thawed
652 }
653 }
654
655 fn add_process(&mut self, thread_group: &ThreadGroup) -> Result<(), Errno> {
656 if self.deleted {
657 return error!(ENOENT);
658 }
659 self.processes.insert(thread_group.leader.clone());
660
661 if self.get_effective_freezer_state() == FreezerState::Frozen {
662 self.freeze_thread_group(&thread_group);
663 }
664 Ok(())
665 }
666
667 fn remove_process(&mut self, thread_group: &ThreadGroup) -> Result<(), Errno> {
668 if self.deleted {
669 return error!(ENOENT);
670 }
671 self.processes.remove(&thread_group.leader);
672
673 if self.get_effective_freezer_state() == FreezerState::Frozen {
674 self.thaw_thread_group(thread_group);
675 }
676 Ok(())
677 }
678
679 fn propagate_freeze(&mut self, inherited_freezer_state: FreezerState) {
680 let prev_effective_freezer_state = self.get_effective_freezer_state();
681 if let Some(freezer) = self.freezer_mut() {
682 freezer.inherited_freezer_state = inherited_freezer_state;
683 }
684 if prev_effective_freezer_state == FreezerState::Frozen {
685 return;
686 }
687
688 for pid in self.processes.iter() {
689 let Ok(thread_group) = pid.get_thread_group() else {
690 continue;
691 };
692 self.freeze_thread_group(&thread_group);
693 }
694
695 for child in self.children.get_children() {
697 let _token = allow_subclass();
701 child.state.lock().propagate_freeze(FreezerState::Frozen);
702 }
703 }
704
705 fn propagate_thaw(&mut self, inherited_freezer_state: FreezerState) {
706 if let Some(freezer) = self.freezer_mut() {
707 freezer.inherited_freezer_state = inherited_freezer_state;
708 }
709 if self.get_effective_freezer_state() == FreezerState::Thawed {
710 self.wait_queue.notify_all();
711 for child in self.children.get_children() {
712 let _token = allow_subclass();
716 child.state.lock().propagate_thaw(FreezerState::Thawed);
717 }
718 }
719 }
720
721 fn propagate_kill(&self) {
722 for pid in self.processes.iter() {
723 let Ok(thread_group) = pid.get_thread_group() else {
724 continue;
725 };
726 thread_group.write().send_signal(SignalInfo::kernel(SIGKILL));
727 }
728
729 for child in self.children.get_children() {
731 let _token = allow_subclass();
735 child.state.lock().propagate_kill();
736 }
737 }
738}
739
740#[derive(Debug)]
742pub struct Cgroup {
743 root: Weak<CgroupRoot>,
744
745 id: u64,
747
748 name: FsString,
750
751 parent: Option<Weak<Cgroup>>,
754
755 state: LockDepMutex<CgroupState, CgroupStateLock>,
757
758 weak_self: Weak<Cgroup>,
759}
760pub type CgroupHandle = Arc<Cgroup>;
761
762pub fn path_from_root(weak_cgroup: Option<Weak<Cgroup>>) -> Result<FsString, Errno> {
764 let cgroup = match weak_cgroup {
765 Some(weak_cgroup) => Weak::upgrade(&weak_cgroup).ok_or_else(|| errno!(ENODEV))?,
766 None => return Ok("/".into()),
767 };
768 let mut path = PathBuilder::new();
769 let mut current = Some(cgroup);
770 while let Some(cgroup) = current {
771 path.prepend_element(cgroup.name());
772 current = cgroup.parent()?;
773 }
774 Ok(path.build_absolute())
775}
776
777impl Cgroup {
778 pub fn new(
779 id: u64,
780 name: &FsStr,
781 root: &Weak<CgroupRoot>,
782 parent: Option<Weak<Cgroup>>,
783 ) -> CgroupHandle {
784 let root_shared = root.upgrade().expect("root must exist");
785 let mut controllers = HashMap::new();
786 for controller in &root_shared.controllers {
787 match controller {
788 ControllerType::Cpuset => {
789 controllers.insert(
790 *controller,
791 ControllerState::Cpuset(CpusetControllerState::default()),
792 );
793 }
794 ControllerType::Freezer => {
795 controllers.insert(
796 *controller,
797 ControllerState::Freezer(FreezerControllerState::default()),
798 );
799 }
800 _ => {}
801 }
802 }
803
804 Arc::new_cyclic(|weak| Self {
805 id,
806 root: root.clone(),
807 name: name.to_owned(),
808 parent,
809 state: LockDepMutex::new(CgroupState {
810 children: Default::default(),
811 processes: Default::default(),
812 deleted: false,
813 wait_queue: Default::default(),
814 controllers,
815 }),
816 weak_self: weak.clone(),
817 })
818 }
819
820 pub fn name(&self) -> &FsStr {
821 self.name.as_ref()
822 }
823
824 fn root(&self) -> Result<Arc<CgroupRoot>, Errno> {
825 self.root.upgrade().ok_or_else(|| errno!(ENODEV))
826 }
827
828 fn parent(&self) -> Result<Option<CgroupHandle>, Errno> {
831 self.parent.as_ref().map(|weak| weak.upgrade().ok_or_else(|| errno!(ENODEV))).transpose()
832 }
833
834 fn count_descendants(&self) -> u64 {
835 self.state.lock().children.count_descendants()
836 }
837
838 fn is_controller_supported(&self, controller: ControllerType) -> bool {
839 self.root().map(|r| r.controllers.contains(&controller)).unwrap_or(false)
840 }
841
842 fn cpuset_path(&self, root: &CgroupRoot) -> Option<String> {
843 if !root.has_controller(ControllerType::Cpuset) {
844 return None;
845 }
846 let bytes = path_from_root(Some(self.weak_self.clone())).ok()?;
847 std::str::from_utf8(&bytes).ok().map(|s| s.to_string())
848 }
849}
850
851impl CgroupOps for Cgroup {
852 fn id(&self) -> u64 {
853 self.id
854 }
855
856 fn add_process(&self, thread_group: &ThreadGroup) -> Result<(), Errno> {
857 let root = self.root()?;
858 let mut pid_table = root.pid_table.lock();
859 match pid_table.entry(thread_group.leader.clone()) {
860 hash_map::Entry::Occupied(mut entry) => {
861 if std::ptr::eq(self, entry.get().as_ptr()) {
864 return Ok(());
865 }
866
867 track_stub!(TODO("https://fxbug.dev/383374687"), "check permissions");
869 if let Some(other_cgroup) = entry.get().upgrade() {
870 other_cgroup.state.lock().remove_process(thread_group)?;
871 }
872
873 self.state.lock().add_process(thread_group)?;
874 entry.insert(self.weak_self.clone());
875 }
876 hash_map::Entry::Vacant(entry) => {
877 self.state.lock().add_process(thread_group)?;
878 entry.insert(self.weak_self.clone());
879 }
880 }
881
882 let tasks = thread_group.read().tasks();
883 if let Some(cpuset_path) = self.cpuset_path(&root) {
884 for task in &tasks {
885 task.write().cpuset_path = cpuset_path.clone();
886 }
887 }
888
889 for task in tasks {
891 if let Err(e) = task.sync_scheduler_state_to_role() {
892 log_warn!("Failed to set thread role for task {}: {:?}", task.tid, e);
893 }
894 }
895
896 Ok(())
897 }
898
899 fn new_child(&self, name: &FsStr) -> Result<CgroupHandle, Errno> {
900 let id = self.root()?.get_next_id();
901 let new_child = Cgroup::new(id, name, &self.root, Some(self.weak_self.clone()));
902 let mut state = self.state.lock();
903 if state.deleted {
904 return error!(ENOENT);
905 }
906 let effective_freezer = state.get_effective_freezer_state();
911 let _token = allow_subclass();
912 if let Some(freezer) = new_child.state.lock().freezer_mut() {
913 freezer.inherited_freezer_state = effective_freezer;
914 }
915 state.children.insert_child(name.into(), new_child)
916 }
917
918 fn get_child(&self, name: &FsStr) -> Result<CgroupHandle, Errno> {
919 let state = self.state.lock();
920 state.children.get_child(name).ok_or_else(|| errno!(ENOENT))
921 }
922
923 fn remove_child(&self, name: &FsStr) -> Result<CgroupHandle, Errno> {
924 let mut state = self.state.lock();
925 if state.deleted {
926 return error!(ENOENT);
927 }
928 state.children.remove_child(name)
929 }
930
931 fn get_children(&self) -> Result<Vec<CgroupHandle>, Errno> {
932 let state = self.state.lock();
933 if state.deleted {
934 return error!(ENOENT);
935 }
936 Ok(state.children.get_children())
937 }
938
939 fn get_pids(&self, _kernel: &Kernel) -> Vec<pid_t> {
940 let mut state = self.state.lock();
941 state.update_processes();
942 state
943 .processes
944 .iter()
945 .filter_map(|v| v.get_thread_group().ok().map(|tg| tg.leader.id))
946 .collect()
947 }
948
949 fn kill(&self) {
950 fuchsia_trace::duration!(CATEGORY_STARNIX, "CgroupKill");
951 let state = self.state.lock();
952 state.propagate_kill();
953 }
954
955 fn is_populated(&self) -> bool {
956 let mut state = self.state.lock();
957 if state.deleted {
958 return false;
959 }
960 state.update_processes();
961 if !state.processes.is_empty() {
962 return true;
963 }
964
965 state.children.get_children().into_iter().any(|child| {
966 let _token = allow_subclass();
970 child.is_populated()
971 })
972 }
973
974 fn freezer(&self) -> Option<&dyn FreezerOps> {
975 if self.is_controller_supported(ControllerType::Freezer) { Some(self) } else { None }
976 }
977
978 fn cpuset(&self) -> Option<&dyn CpusetOps> {
979 if self.is_controller_supported(ControllerType::Cpuset) { Some(self) } else { None }
980 }
981}
982
983impl FreezerOps for Cgroup {
984 fn get_freezer_state(&self) -> CgroupFreezerState {
985 let state = self.state.lock();
986 let self_freezer_state = state.freezer().map(|f| f.self_freezer_state).unwrap_or_default();
987 CgroupFreezerState {
988 self_freezer_state,
989 effective_freezer_state: state.get_effective_freezer_state(),
990 }
991 }
992
993 fn freeze(&self) {
994 fuchsia_trace::duration!(CATEGORY_STARNIX, "CgroupFreeze");
995 let mut state = self.state.lock();
996 let inherited_freezer_state =
997 state.freezer().map(|f| f.inherited_freezer_state).unwrap_or_default();
998 state.propagate_freeze(inherited_freezer_state);
999 if let Some(freezer) = state.freezer_mut() {
1000 freezer.self_freezer_state = FreezerState::Frozen;
1001 }
1002 }
1003
1004 fn thaw(&self) {
1005 fuchsia_trace::duration!(CATEGORY_STARNIX, "CgroupThaw");
1006 let mut state = self.state.lock();
1007 if let Some(freezer) = state.freezer_mut() {
1008 freezer.self_freezer_state = FreezerState::Thawed;
1009 }
1010 let inherited_freezer_state =
1011 state.freezer().map(|f| f.inherited_freezer_state).unwrap_or_default();
1012 state.propagate_thaw(inherited_freezer_state);
1013 }
1014}
1015
1016impl CpusetOps for Cgroup {
1017 fn cpuset_cpus(&self) -> Vec<u32> {
1018 self.state
1019 .lock()
1020 .cpuset()
1021 .and_then(|c| c.cpus.clone())
1022 .unwrap_or_else(|| (0..zx::system_get_num_cpus()).collect())
1023 }
1024
1025 fn set_cpuset_cpus(&self, cpus: Vec<u32>) {
1026 let mut state = self.state.lock();
1028 if let Some(cpuset) = state.cpuset_mut() {
1029 cpuset.cpus = Some(cpus);
1030 }
1031 }
1032}
1033
1034#[cfg(test)]
1035mod test {
1036 use super::*;
1037 use crate::testing::spawn_kernel_and_run;
1038 use assert_matches::assert_matches;
1039 use starnix_uapi::signals::SIGCHLD;
1040 use starnix_uapi::{CLONE_SIGHAND, CLONE_THREAD, CLONE_VM};
1041
1042 #[::fuchsia::test]
1043 async fn cgroup_path_from_root() {
1044 spawn_kernel_and_run(async |_| {
1045 let root = CgroupRoot::new(0, BTreeSet::new());
1046
1047 let test_cgroup =
1048 root.new_child("test".into()).expect("new_child on root cgroup succeeds");
1049 let child_cgroup = test_cgroup
1050 .new_child("child".into())
1051 .expect("new_child on non-root cgroup succeeds");
1052
1053 assert_eq!(path_from_root(Some(Arc::downgrade(&test_cgroup))), Ok("/test".into()));
1054 assert_eq!(
1055 path_from_root(Some(Arc::downgrade(&child_cgroup))),
1056 Ok("/test/child".into())
1057 );
1058 })
1059 .await;
1060 }
1061
1062 #[::fuchsia::test]
1063 async fn cgroup_clone_task_in_frozen_cgroup() {
1064 spawn_kernel_and_run(async |current_task| {
1065 let kernel = current_task.kernel();
1066 let root = &kernel.cgroups.cgroup2;
1067 let cgroup = root.new_child("test".into()).expect("new_child on root cgroup succeeds");
1068
1069 let process = current_task.clone_task_for_test(0, Some(SIGCHLD));
1070 cgroup.add_process(process.thread_group()).expect("add process to cgroup");
1071 cgroup.freeze();
1072 assert_eq!(cgroup.get_pids(&kernel).first(), Some(process.get_pid()).as_ref());
1073 assert_eq!(root.get_cgroup(&process.pid).unwrap().as_ptr(), Arc::as_ptr(&cgroup));
1074
1075 let thread = process.clone_task_for_test(
1076 (CLONE_THREAD | CLONE_SIGHAND | CLONE_VM) as u64,
1077 Some(SIGCHLD),
1078 );
1079
1080 let thread_state = thread.read();
1081 let kernel_signals = thread_state.kernel_signals_for_test();
1082 assert_matches!(kernel_signals.front(), Some(KernelSignal::Freeze(_)));
1083 })
1084 .await;
1085 }
1086
1087 #[::fuchsia::test]
1088 async fn cgroup_tg_release_removes_pid() {
1089 spawn_kernel_and_run(async |current_task| {
1090 let kernel = current_task.kernel();
1091 let root = &kernel.cgroups.cgroup2;
1092 let cgroup = root.new_child("test".into()).expect("new_child on root cgroup succeeds");
1093
1094 let process = current_task.clone_task_for_test(0, Some(SIGCHLD));
1095 cgroup.add_process(process.thread_group()).expect("add process to cgroup");
1096
1097 assert_eq!(root.get_cgroup(&process.pid).unwrap().as_ptr(), Arc::as_ptr(&cgroup));
1098
1099 drop(process);
1101
1102 assert!(root.pid_table.lock().is_empty());
1104 })
1105 .await;
1106 }
1107}