kernel/task/
namespace.rs

1//! Task namespace module.
2//!
3//! This module provides task ID namespace functionality, allowing different
4//! ABI modules to maintain separate task ID spaces while preserving parent-child
5//! relationships and global task management.
6
7extern crate alloc;
8
9use alloc::collections::BTreeMap;
10use alloc::sync::Arc;
11use spin::Mutex;
12
13/// Task namespace for managing task IDs within a specific context.
14///
15/// Each namespace maintains its own task ID counter and can be used by
16/// ABI modules to implement their own task ID systems (e.g., Linux PID
17/// namespace, xv6 process IDs, etc.).
18///
19/// Namespaces form a hierarchy where child namespaces inherit from parent
20/// namespaces, allowing tasks in child namespaces to be visible from parent
21/// namespaces with potentially different IDs.
22#[derive(Debug)]
23pub struct TaskNamespace {
24    /// Unique identifier for this namespace
25    id: usize,
26    /// Next task ID to allocate in this namespace
27    next_task_id: Mutex<usize>,
28    /// Mapping from namespace-local task IDs to global task IDs.
29    ///
30    /// This enables syscall boundary translation (PID namespace semantics)
31    /// while keeping kernel internals globally-addressed.
32    local_to_global: Mutex<BTreeMap<usize, usize>>,
33    /// Reverse mapping from global task IDs to namespace-local task IDs.
34    global_to_local: Mutex<BTreeMap<usize, usize>>,
35    /// Parent namespace (None for root namespace)
36    parent: Option<Arc<TaskNamespace>>,
37    /// Name/description of this namespace (for debugging)
38    name: alloc::string::String,
39}
40
41impl TaskNamespace {
42    /// Create a new root namespace.
43    ///
44    /// # Arguments
45    /// * `name` - Name/description of this namespace
46    ///
47    /// # Returns
48    /// A new root namespace with ID 0
49    pub fn new_root(name: alloc::string::String) -> Arc<Self> {
50        Arc::new(TaskNamespace {
51            id: 0,
52            next_task_id: Mutex::new(1), // Start from 1 (0 is often reserved)
53            local_to_global: Mutex::new(BTreeMap::new()),
54            global_to_local: Mutex::new(BTreeMap::new()),
55            parent: None,
56            name,
57        })
58    }
59
60    /// Create a new child namespace.
61    ///
62    /// # Arguments
63    /// * `parent` - Parent namespace
64    /// * `name` - Name/description of this namespace
65    ///
66    /// # Returns
67    /// A new child namespace
68    pub fn new_child(parent: Arc<TaskNamespace>, name: alloc::string::String) -> Arc<Self> {
69        static NEXT_NS_ID: Mutex<usize> = Mutex::new(1);
70        let ns_id = {
71            let mut id = NEXT_NS_ID.lock();
72            let current = *id;
73            *id += 1;
74            current
75        };
76
77        Arc::new(TaskNamespace {
78            id: ns_id,
79            next_task_id: Mutex::new(1),
80            local_to_global: Mutex::new(BTreeMap::new()),
81            global_to_local: Mutex::new(BTreeMap::new()),
82            parent: Some(parent),
83            name,
84        })
85    }
86
87    /// Allocate a new task ID in this namespace.
88    ///
89    /// # Returns
90    /// The next available task ID in this namespace
91    pub fn allocate_task_id(&self) -> usize {
92        let mut next_id = self.next_task_id.lock();
93        let id = *next_id;
94        *next_id += 1;
95        id
96    }
97
98    /// Allocate a new namespace-local task ID and register it for a global task.
99    ///
100    /// This is the preferred allocator when a task is created or enters this namespace,
101    /// because syscalls need a stable local↔global mapping.
102    pub fn allocate_task_id_for(&self, global_task_id: usize) -> usize {
103        let local_id = self.allocate_task_id();
104        self.register_mapping(local_id, global_task_id);
105        local_id
106    }
107
108    /// Register an existing namespace-local ID mapping for a global task ID.
109    pub fn register_mapping(&self, local_id: usize, global_task_id: usize) {
110        self.local_to_global.lock().insert(local_id, global_task_id);
111        self.global_to_local.lock().insert(global_task_id, local_id);
112    }
113
114    /// Resolve a namespace-local task ID to a global task ID.
115    pub fn resolve_global_id(&self, local_id: usize) -> Option<usize> {
116        self.local_to_global.lock().get(&local_id).copied()
117    }
118
119    /// Resolve a global task ID to a namespace-local task ID.
120    pub fn resolve_local_id(&self, global_task_id: usize) -> Option<usize> {
121        self.global_to_local.lock().get(&global_task_id).copied()
122    }
123
124    /// Get the namespace ID.
125    pub fn get_id(&self) -> usize {
126        self.id
127    }
128
129    /// Get the parent namespace, if any.
130    pub fn get_parent(&self) -> Option<&Arc<TaskNamespace>> {
131        self.parent.as_ref()
132    }
133
134    /// Get the namespace name.
135    pub fn get_name(&self) -> &str {
136        &self.name
137    }
138
139    /// Check if this namespace is the root namespace.
140    pub fn is_root(&self) -> bool {
141        self.parent.is_none()
142    }
143}
144
145/// Global root task namespace.
146///
147/// This is the default namespace used by all tasks unless explicitly
148/// assigned to a different namespace.
149static ROOT_NAMESPACE: spin::Once<Arc<TaskNamespace>> = spin::Once::new();
150
151/// Get the global root task namespace.
152pub fn get_root_namespace() -> &'static Arc<TaskNamespace> {
153    ROOT_NAMESPACE.call_once(|| TaskNamespace::new_root("root".into()))
154}
155
156#[cfg(test)]
157mod tests {
158    use super::*;
159    use alloc::string::ToString;
160
161    #[test_case]
162    fn test_root_namespace_creation() {
163        let root = TaskNamespace::new_root("test_root".to_string());
164        assert_eq!(root.get_id(), 0);
165        assert_eq!(root.get_name(), "test_root");
166        assert!(root.is_root());
167        assert!(root.get_parent().is_none());
168    }
169
170    #[test_case]
171    fn test_child_namespace_creation() {
172        let root = TaskNamespace::new_root("root".to_string());
173        let child = TaskNamespace::new_child(root.clone(), "child".to_string());
174
175        assert_ne!(child.get_id(), 0);
176        assert_eq!(child.get_name(), "child");
177        assert!(!child.is_root());
178        assert!(child.get_parent().is_some());
179    }
180
181    #[test_case]
182    fn test_task_id_allocation() {
183        let ns = TaskNamespace::new_root("test".to_string());
184        let id1 = ns.allocate_task_id();
185        let id2 = ns.allocate_task_id();
186        let id3 = ns.allocate_task_id();
187
188        assert_eq!(id1, 1);
189        assert_eq!(id2, 2);
190        assert_eq!(id3, 3);
191    }
192
193    #[test_case]
194    fn test_separate_namespace_ids() {
195        let ns1 = TaskNamespace::new_root("ns1".to_string());
196        let ns2 = TaskNamespace::new_root("ns2".to_string());
197
198        let id1_1 = ns1.allocate_task_id();
199        let id2_1 = ns2.allocate_task_id();
200        let id1_2 = ns1.allocate_task_id();
201        let id2_2 = ns2.allocate_task_id();
202
203        // Both namespaces should allocate IDs independently
204        assert_eq!(id1_1, 1);
205        assert_eq!(id2_1, 1);
206        assert_eq!(id1_2, 2);
207        assert_eq!(id2_2, 2);
208    }
209
210    #[test_case]
211    fn test_root_namespace_singleton() {
212        let root1 = get_root_namespace();
213        let root2 = get_root_namespace();
214
215        // Should return the same instance
216        assert_eq!(Arc::as_ptr(root1), Arc::as_ptr(root2));
217    }
218}