starnix_modules_cgroupfs/
fs.rs1use starnix_core::task::{Cgroup, CgroupOps, CgroupRoot, CurrentTask};
6use starnix_core::vfs::{
7 CacheMode, FileSystem, FileSystemHandle, FileSystemOps, FileSystemOptions, FsNodeHandle,
8 FsNodeInfo, FsStr,
9};
10use starnix_sync::{FileOpsCore, LockEqualOrBefore, Locked, Mutex, Unlocked};
11use starnix_types::vfs::default_statfs;
12use starnix_uapi::auth::FsCred;
13use starnix_uapi::errors::Errno;
14use starnix_uapi::{CGROUP_SUPER_MAGIC, CGROUP2_SUPER_MAGIC, errno, mode, statfs};
15
16use std::collections::HashMap;
17use std::sync::{Arc, Weak};
18
19use crate::directory::{CgroupDirectory, CgroupDirectoryHandle};
20
21pub struct CgroupV1Fs {
22 pub root: Arc<CgroupRoot>,
23
24 pub dir_nodes: Arc<DirectoryNodes>,
26}
27
28impl CgroupV1Fs {
29 pub fn new_fs(
30 locked: &mut Locked<Unlocked>,
31 current_task: &CurrentTask,
32 options: FileSystemOptions,
33 ) -> Result<FileSystemHandle, Errno> {
34 let kernel = current_task.kernel();
35 let root = CgroupRoot::new();
36 let dir_nodes = DirectoryNodes::new(Arc::downgrade(&root));
37 let root_dir = dir_nodes.root.clone();
38 let fs = FileSystem::new(
39 locked,
40 kernel,
41 CacheMode::Uncached,
42 CgroupV1Fs { dir_nodes, root },
43 options,
44 )?;
45 root_dir.create_root_interface_files(&fs);
46 let root_ino = fs.allocate_ino();
47 fs.create_root(root_ino, root_dir);
48 Ok(fs)
49 }
50}
51impl FileSystemOps for CgroupV1Fs {
52 fn name(&self) -> &'static FsStr {
53 b"cgroup".into()
54 }
55 fn statfs(
56 &self,
57 _locked: &mut Locked<FileOpsCore>,
58 _fs: &FileSystem,
59 _current_task: &CurrentTask,
60 ) -> Result<statfs, Errno> {
61 Ok(default_statfs(CGROUP_SUPER_MAGIC))
62 }
63}
64
65pub struct CgroupV2Fs {
66 pub dir_nodes: Arc<DirectoryNodes>,
68}
69
70struct CgroupV2FsHandle(FileSystemHandle);
71pub fn cgroup2_fs(
72 locked: &mut Locked<Unlocked>,
73 current_task: &CurrentTask,
74 options: FileSystemOptions,
75) -> Result<FileSystemHandle, Errno> {
76 Ok(current_task
77 .kernel()
78 .expando
79 .get_or_try_init(|| {
80 Ok(CgroupV2FsHandle(CgroupV2Fs::new_fs(locked, current_task, options)?))
81 })?
82 .0
83 .clone())
84}
85
86impl CgroupV2Fs {
87 fn new_fs<L>(
88 locked: &mut Locked<L>,
89 current_task: &CurrentTask,
90 options: FileSystemOptions,
91 ) -> Result<FileSystemHandle, Errno>
92 where
93 L: LockEqualOrBefore<FileOpsCore>,
94 {
95 let kernel = current_task.kernel();
96 let dir_nodes = DirectoryNodes::new(Arc::downgrade(&kernel.cgroups.cgroup2));
97 let root = dir_nodes.root.clone();
98 let fs = FileSystem::new(
99 locked,
100 kernel,
101 CacheMode::Uncached,
102 CgroupV2Fs { dir_nodes },
103 options,
104 )?;
105 root.create_root_interface_files(&fs);
106 let root_ino = fs.allocate_ino();
107 fs.create_root(root_ino, root);
108 Ok(fs)
109 }
110}
111
112impl FileSystemOps for CgroupV2Fs {
113 fn name(&self) -> &'static FsStr {
114 b"cgroup2".into()
115 }
116 fn statfs(
117 &self,
118 _locked: &mut Locked<FileOpsCore>,
119 _fs: &FileSystem,
120 _current_task: &CurrentTask,
121 ) -> Result<statfs, Errno> {
122 Ok(default_statfs(CGROUP2_SUPER_MAGIC))
123 }
124}
125
126pub struct DirectoryNodes {
128 root: CgroupDirectoryHandle,
131
132 nodes: Mutex<HashMap<u64, FsNodeHandle>>,
135}
136
137impl DirectoryNodes {
138 pub fn new(root_cgroup: Weak<CgroupRoot>) -> Arc<DirectoryNodes> {
139 Arc::new_cyclic(|weak_self| Self {
140 root: CgroupDirectory::new_root(root_cgroup, weak_self.clone()),
141 nodes: Mutex::new(HashMap::new()),
142 })
143 }
144
145 pub fn get_node(&self, cgroup: &Arc<Cgroup>) -> Result<FsNodeHandle, Errno> {
147 let nodes = self.nodes.lock();
148 nodes.get(&cgroup.id()).cloned().ok_or_else(|| errno!(ENOENT))
149 }
150
151 pub fn get_nodes(&self, cgroups: &Vec<Arc<Cgroup>>) -> Vec<Option<FsNodeHandle>> {
153 let nodes = self.nodes.lock();
154 cgroups.iter().map(|cgroup| nodes.get(&cgroup.id()).cloned()).collect()
155 }
156
157 pub fn add_node(
159 &self,
160 cgroup: &Arc<Cgroup>,
161 directory: CgroupDirectoryHandle,
162 fs: &FileSystemHandle,
163 ) -> FsNodeHandle {
164 let id = cgroup.id();
165 let node = fs.create_node_and_allocate_node_id(
166 directory,
167 FsNodeInfo::new(mode!(IFDIR, 0o755), FsCred::root()),
168 );
169 let mut nodes = self.nodes.lock();
170 nodes.insert(id, node.clone());
171 node
172 }
173
174 pub fn remove_node(&self, cgroup: &Arc<Cgroup>) -> Result<FsNodeHandle, Errno> {
176 let id = cgroup.id();
177 let mut nodes = self.nodes.lock();
178 nodes.remove(&id).ok_or_else(|| errno!(ENOENT))
179 }
180}
181
182#[cfg(test)]
183mod test {
184 use super::*;
185
186 #[allow(deprecated, reason = "pre-existing usage")]
187 use starnix_core::testing::create_kernel_task_and_unlocked;
188 use starnix_core::vfs::fs_registry::FsRegistry;
189
190 #[::fuchsia::test]
191 async fn test_filesystem_creates_nodes() {
192 #[allow(deprecated, reason = "pre-existing usage")]
193 let (kernel, current_task, locked) = create_kernel_task_and_unlocked();
194 let registry = kernel.expando.get::<FsRegistry>();
195 registry.register(b"cgroup2".into(), cgroup2_fs);
196
197 let fs = current_task
198 .create_filesystem(locked, b"cgroup2".into(), Default::default())
199 .expect("create_filesystem");
200
201 let cgroupfs = fs.downcast_ops::<CgroupV2Fs>().expect("downcast_ops");
202 let dir_nodes = cgroupfs.dir_nodes.clone();
203 assert!(dir_nodes.nodes.lock().is_empty(), "new filesystem does not contain nodes");
204
205 let root_dir = dir_nodes.root.clone();
206 assert!(root_dir.has_interface_files(), "root directory is initialized");
207 }
208}