Skip to main content

starnix_core/vfs/
fs_node_cache.rs

1// Copyright 2025 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 crate::vfs::{FsNode, FsNodeHandle, WeakFsNodeHandle};
6use fuchsia_rcu::RcuReadScope;
7use starnix_lifecycle::AtomicCounter;
8use starnix_rcu::rcu_hash_map::{Entry, RcuHashMap};
9use starnix_uapi::errors::Errno;
10use starnix_uapi::ino_t;
11use std::ops::Range;
12use std::sync::Arc;
13
14pub struct FsNodeCache {
15    /// The next node ID to allocate.
16    next_ino: Option<AtomicCounter<u64>>,
17
18    /// The FsNodes that have been created for this file system.
19    nodes: RcuHashMap<ino_t, WeakFsNodeHandle>,
20}
21
22impl Default for FsNodeCache {
23    fn default() -> Self {
24        Self::new(false)
25    }
26}
27
28impl FsNodeCache {
29    pub fn new(uses_external_node_ids: bool) -> Self {
30        Self {
31            next_ino: if uses_external_node_ids {
32                None
33            } else {
34                Some(AtomicCounter::<u64>::new(1))
35            },
36            nodes: RcuHashMap::default(),
37        }
38    }
39
40    pub fn uses_external_node_ids(&self) -> bool {
41        self.next_ino.is_none()
42    }
43
44    pub fn allocate_ino(&self) -> Option<ino_t> {
45        self.next_ino.as_ref().map(|counter| counter.next())
46    }
47
48    pub fn allocate_ino_range(&self, size: usize) -> Option<Range<ino_t>> {
49        assert!(size > 0);
50        self.next_ino.as_ref().map(|counter| {
51            let start = counter.add(size as u64);
52            Range { start: start as ino_t, end: start + size as ino_t }
53        })
54    }
55
56    pub fn insert_node(&self, node: &FsNodeHandle) {
57        let node_key = node.node_key();
58        self.nodes.insert(node_key, Arc::downgrade(node));
59    }
60
61    pub fn remove_node(&self, node: &FsNode) {
62        let node_key = node.node_key();
63        let mut nodes = self.nodes.lock();
64        if let Some(weak_node) = nodes.get(&node_key) {
65            if weak_node.strong_count() == 0 {
66                nodes.remove(&node_key);
67            }
68        }
69    }
70
71    pub fn get_and_validate_or_create_node<V, C>(
72        &self,
73        node_key: ino_t,
74        validate_fn: V,
75        create_fn: C,
76    ) -> Result<FsNodeHandle, Errno>
77    where
78        V: Fn(&FsNodeHandle) -> bool,
79        C: FnOnce() -> Result<FsNodeHandle, Errno>,
80    {
81        // Optimistic check with RCU read lock.
82        {
83            let scope = RcuReadScope::new();
84            if let Some(weak_node) = self.nodes.get(&scope, &node_key) {
85                if let Some(node) = weak_node.upgrade() {
86                    if validate_fn(&node) {
87                        return Ok(node);
88                    }
89                }
90            }
91        }
92
93        // Slow path: acquire the lock.
94        let mut nodes = self.nodes.lock();
95        match nodes.entry(node_key) {
96            Entry::Vacant(entry) => {
97                let node = create_fn()?;
98                entry.insert(Arc::downgrade(&node));
99                Ok(node)
100            }
101            Entry::Occupied(mut entry) => {
102                if let Some(node) = entry.get().upgrade() {
103                    if validate_fn(&node) {
104                        return Ok(node);
105                    }
106                }
107                let node = create_fn()?;
108                entry.insert(Arc::downgrade(&node));
109                Ok(node)
110            }
111        }
112    }
113}