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        owner: FsCred,
164    ) -> FsNodeHandle {
165        let id = cgroup.id();
166        let node = fs.create_node_and_allocate_node_id(
167            directory,
168            FsNodeInfo::new(mode!(IFDIR, 0o755), owner),
169        );
170        let mut nodes = self.nodes.lock();
171        nodes.insert(id, node.clone());
172        node
173    }
174
175    /// Removes an entry from `nodes`, errors if not found.
176    pub fn remove_node(&self, cgroup: &Arc<Cgroup>) -> Result<FsNodeHandle, Errno> {
177        let id = cgroup.id();
178        let mut nodes = self.nodes.lock();
179        nodes.remove(&id).ok_or_else(|| errno!(ENOENT))
180    }
181}
182
183#[cfg(test)]
184mod test {
185    use super::*;
186
187    #[allow(deprecated, reason = "pre-existing usage")]
188    use starnix_core::testing::create_kernel_task_and_unlocked;
189    use starnix_core::vfs::fs_registry::FsRegistry;
190
191    #[::fuchsia::test]
192    async fn test_filesystem_creates_nodes() {
193        #[allow(deprecated, reason = "pre-existing usage")]
194        let (kernel, current_task, locked) = create_kernel_task_and_unlocked();
195        let registry = kernel.expando.get::<FsRegistry>();
196        registry.register(b"cgroup2".into(), cgroup2_fs);
197
198        let fs = current_task
199            .create_filesystem(locked, b"cgroup2".into(), Default::default())
200            .expect("create_filesystem");
201
202        let cgroupfs = fs.downcast_ops::<CgroupV2Fs>().expect("downcast_ops");
203        let dir_nodes = cgroupfs.dir_nodes.clone();
204        assert!(dir_nodes.nodes.lock().is_empty(), "new filesystem does not contain nodes");
205
206        let root_dir = dir_nodes.root.clone();
207        assert!(root_dir.has_interface_files(), "root directory is initialized");
208    }
209}