Skip to main content

starnix_modules_cgroupfs/
fs.rs

1// Copyright 2024 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use 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    /// All directory nodes of the filesystem.
25    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    /// All directory nodes of the filesystem.
67    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
126/// Represents all directory nodes of a cgroup hierarchy.
127pub struct DirectoryNodes {
128    /// `CgroupRoot`'s directory handle. The `FileSystem` owns the `FsNode` of the root, and so we
129    /// do not have a `FsNodeHandle` of the root.
130    root: CgroupDirectoryHandle,
131
132    /// All non-root cgroup directories, keyed by cgroup's ID. Every non-root cgroup has a
133    /// corresponding node.
134    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    /// Looks for the corresponding node in the filesystem, errors if not found.
146    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    /// Returns the corresponding nodes for a set of cgroups.
152    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    /// Creates a new `FsNode` for `directory` and stores it in `nodes`.
158    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    /// Removes an entry from `nodes`, errors if not found.
175    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}