kernel/ipc/
shared_memory.rs

1//! Shared memory implementation for inter-process communication
2//!
3//! This module provides shared memory objects for memory-based communication between processes.
4//! Shared memory allows multiple processes to access the same physical memory region,
5//! providing efficient data sharing without copying.
6
7use alloc::{format, string::String, sync::Arc, vec::Vec};
8use spin::RwLock;
9
10use crate::mem::page::{allocate_raw_pages, free_raw_pages};
11use crate::object::capability::memory_mapping::{
12    AccessKind, MemoryMappingOps, ResolveFaultError, ResolveFaultResult,
13};
14use crate::vm::vmem::VirtualMemoryMap;
15
16const LOG_SHARED_MEMORY_RESIZE: bool = false;
17
18/// Shared memory operations
19///
20/// This trait extends the base functionality for shared memory objects.
21pub trait SharedMemoryObject: MemoryMappingOps + Send + Sync {
22    /// Get the size of the shared memory region in bytes
23    fn size(&self) -> usize;
24
25    /// Resize the shared memory region (within capacity)
26    fn resize(&self, new_size: usize) -> Result<(), &'static str>;
27
28    /// Get a unique identifier for this shared memory object
29    fn id(&self) -> String;
30
31    /// Check if the shared memory is still valid
32    fn is_valid(&self) -> bool;
33}
34
35/// Internal state of a shared memory object
36struct SharedMemoryState {
37    /// Physical address of the shared memory region
38    paddr: usize,
39    /// Size of the shared memory region in bytes
40    size: usize,
41    /// Allocated capacity of the shared memory region in bytes
42    capacity: usize,
43    /// Access permissions for the shared memory
44    permissions: usize,
45    /// Whether this shared memory is still valid
46    valid: bool,
47    /// Number of active mappings
48    mapping_count: usize,
49    /// Old allocations kept alive while mappings still exist
50    stale_pages: Vec<(usize, usize)>,
51    /// Whether this object owns the physical memory (should free on drop)
52    owns_memory: bool,
53}
54
55impl SharedMemoryState {
56    fn new(paddr: usize, size: usize, permissions: usize, owns_memory: bool) -> Self {
57        Self {
58            paddr,
59            size,
60            capacity: size,
61            permissions,
62            valid: true,
63            mapping_count: 0,
64            stale_pages: Vec::new(),
65            owns_memory,
66        }
67    }
68}
69
70/// A shared memory object for inter-process communication
71///
72/// SharedMemory provides a memory region that can be mapped into multiple
73/// processes' address spaces, allowing efficient data sharing without copying.
74pub struct SharedMemory {
75    /// Shared state of the memory object
76    state: Arc<RwLock<SharedMemoryState>>,
77    /// Unique identifier for debugging
78    id: String,
79}
80
81impl SharedMemory {
82    /// Create a new shared memory object with the specified size and permissions
83    ///
84    /// # Arguments
85    /// * `size` - Size of the shared memory region in bytes (will be rounded up to page size)
86    /// * `permissions` - Access permissions (read/write/execute flags)
87    ///
88    /// # Returns
89    /// A new shared memory object, or an error if allocation fails
90    pub fn new(size: usize, permissions: usize) -> Result<Self, &'static str> {
91        use crate::environment::PAGE_SIZE;
92
93        if size == 0 {
94            return Err("Size must be greater than 0");
95        }
96
97        // Calculate number of pages needed (round up)
98        let num_pages = (size + PAGE_SIZE - 1) / PAGE_SIZE;
99        let aligned_size = num_pages * PAGE_SIZE;
100
101        // Allocate physical memory for the shared region
102        let pages = allocate_raw_pages(num_pages);
103        if pages.is_null() {
104            return Err("Failed to allocate physical memory for shared memory");
105        }
106        let paddr = pages as usize;
107
108        let state = SharedMemoryState::new(paddr, aligned_size, permissions, true);
109        let id = format!("shmem_{:#x}", paddr);
110
111        Ok(Self {
112            state: Arc::new(RwLock::new(state)),
113            id,
114        })
115    }
116
117    /// Create a shared memory object from an existing physical address
118    ///
119    /// # Arguments
120    /// * `paddr` - Physical address of the memory region
121    /// * `size` - Size of the memory region in bytes
122    /// * `permissions` - Access permissions
123    ///
124    /// # Returns
125    /// A new shared memory object wrapping the existing memory
126    ///
127    /// # Safety
128    /// The caller must ensure that the physical address is valid and the size is correct.
129    /// The physical memory must remain valid for the lifetime of this object.
130    /// This object will NOT free the memory on drop - the caller is responsible for
131    /// managing the memory lifetime.
132    pub unsafe fn from_paddr(paddr: usize, size: usize, permissions: usize) -> Self {
133        let state = SharedMemoryState::new(paddr, size, permissions, false);
134        let id = format!("shmem_{:#x}", paddr);
135
136        Self {
137            state: Arc::new(RwLock::new(state)),
138            id,
139        }
140    }
141
142    /// Invalidate this shared memory object
143    ///
144    /// This marks the shared memory as invalid, preventing new mappings.
145    /// Existing mappings may continue to work but should be unmapped.
146    pub fn invalidate(&self) {
147        let mut state = self.state.write();
148        state.valid = false;
149    }
150}
151
152impl SharedMemoryObject for SharedMemory {
153    fn size(&self) -> usize {
154        self.state.read().size
155    }
156
157    fn resize(&self, new_size: usize) -> Result<(), &'static str> {
158        use crate::environment::PAGE_SIZE;
159
160        let mut state = self.state.write();
161        let aligned_size = if new_size == 0 {
162            0
163        } else {
164            let num_pages = (new_size + PAGE_SIZE - 1) / PAGE_SIZE;
165            num_pages * PAGE_SIZE
166        };
167
168        // 容量内であればサイズ更新のみ
169        if aligned_size <= state.capacity {
170            state.size = aligned_size;
171            return Ok(());
172        }
173
174        if !state.owns_memory {
175            return Err("Shared memory resize not supported for external memory");
176        }
177
178        // 新しいメモリを割り当て
179        let num_pages = aligned_size / PAGE_SIZE;
180        let pages = allocate_raw_pages(num_pages);
181        if pages.is_null() {
182            return Err("Failed to allocate physical memory for shared memory resize");
183        }
184
185        let old_paddr = state.paddr;
186        let copy_size = state.size;
187
188        // 古いデータをコピー
189        if copy_size > 0 {
190            unsafe {
191                core::ptr::copy_nonoverlapping(
192                    state.paddr as *const u8,
193                    pages as *mut u8,
194                    copy_size,
195                );
196            }
197        }
198
199        // 古いメモリを解放
200        let old_pages = state.capacity / PAGE_SIZE;
201        if old_pages > 0 {
202            if state.mapping_count > 0 {
203                state.stale_pages.push((old_paddr, old_pages));
204            } else {
205                let old_ptr = old_paddr as *mut crate::mem::page::Page;
206                free_raw_pages(old_ptr, old_pages);
207            }
208        }
209
210        state.paddr = pages as usize;
211        state.size = aligned_size;
212        state.capacity = aligned_size;
213
214        // if LOG_SHARED_MEMORY_RESIZE {
215        //     crate::println!(
216        //         "[SharedMemory::resize] reallocated: old_paddr={:#x} new_paddr={:#x} mapping_count={}",
217        //         old_paddr,
218        //         state.paddr,
219        //         state.mapping_count
220        //     );
221        // }
222
223        // NOTE: マッピングがある場合、古いpmap.pmarea.startは無効になる
224        // resolve_faultで動的にpaddrを取得するように修正済み
225        // if LOG_SHARED_MEMORY_RESIZE && state.mapping_count > 0 {
226        //     crate::println!(
227        //         "[SharedMemory::resize] WARNING: {} active mappings exist, their pmarea is now stale!",
228        //         state.mapping_count
229        //     );
230        // }
231
232        Ok(())
233    }
234
235    fn id(&self) -> String {
236        self.id.clone()
237    }
238
239    fn is_valid(&self) -> bool {
240        self.state.read().valid
241    }
242}
243
244impl MemoryMappingOps for SharedMemory {
245    fn get_mapping_info(
246        &self,
247        offset: usize,
248        length: usize,
249    ) -> Result<(usize, usize, bool), &'static str> {
250        let state = self.state.read();
251
252        if !state.valid {
253            return Err("Shared memory object is not valid");
254        }
255
256        let end = match offset.checked_add(length) {
257            Some(end) => end,
258            None => {
259                return Err("Mapping request exceeds shared memory size");
260            }
261        };
262
263        if end > state.size {
264            return Err("Mapping request exceeds shared memory size");
265        }
266
267        // Return physical address (base + offset), permissions, and shared flag
268        let paddr = state
269            .paddr
270            .checked_add(offset)
271            .ok_or("Physical address overflow in shared memory mapping")?;
272
273        Ok((paddr, state.permissions, true))
274    }
275
276    fn on_mapped(&self, _vaddr: usize, _paddr: usize, _length: usize, _offset: usize) {
277        let mut state = self.state.write();
278        state.mapping_count += 1;
279    }
280
281    fn on_unmapped(&self, _vaddr: usize, _length: usize) {
282        let mut state = self.state.write();
283        if state.mapping_count > 0 {
284            state.mapping_count -= 1;
285        }
286        if state.mapping_count == 0 && !state.stale_pages.is_empty() {
287            let stale = core::mem::take(&mut state.stale_pages);
288            for (paddr, pages) in stale {
289                if pages == 0 {
290                    continue;
291                }
292                let ptr = paddr as *mut crate::mem::page::Page;
293                free_raw_pages(ptr, pages);
294            }
295        }
296    }
297
298    fn supports_mmap(&self) -> bool {
299        self.state.read().valid
300    }
301
302    fn mmap_owner_name(&self) -> String {
303        self.id.clone()
304    }
305
306    fn resolve_fault(
307        &self,
308        access: &AccessKind,
309        map: &VirtualMemoryMap,
310    ) -> Result<ResolveFaultResult, ResolveFaultError> {
311        let state = self.state.read();
312
313        if !state.valid {
314            return Err(ResolveFaultError::Invalid);
315        }
316
317        // Calculate the page-aligned virtual address
318        let page_vaddr = access.vaddr & !(crate::environment::PAGE_SIZE - 1);
319
320        // vmarea範囲チェック(マッピング時のサイズ)
321        // NOTE: ftruncateでリサイズされた場合、vmarea.endは古いままなので、
322        //       SharedMemoryの現在のsizeも確認する必要がある
323        if page_vaddr < map.vmarea.start {
324            return Err(ResolveFaultError::Unmapped);
325        }
326
327        let offset_in_mapping = page_vaddr - map.vmarea.start;
328
329        // 現在のSharedMemoryサイズを確認(動的に拡張された可能性がある)
330        if offset_in_mapping >= state.size {
331            return Err(ResolveFaultError::Unmapped);
332        }
333
334        // 動的にpaddrを取得(resizeで変更されている可能性があるため)
335        // map.pmarea.startは古い可能性があるので使わない
336        let paddr_page_base = state
337            .paddr
338            .checked_add(offset_in_mapping)
339            .ok_or(ResolveFaultError::Invalid)?;
340
341        Ok(ResolveFaultResult {
342            paddr_page_base,
343            is_tail: false,
344        })
345    }
346}
347
348impl Drop for SharedMemory {
349    fn drop(&mut self) {
350        use crate::environment::PAGE_SIZE;
351
352        let state = self.state.read();
353        if state.mapping_count > 0 {
354            // Note: In a real implementation, we should ensure all mappings are
355            // unmapped before freeing the physical memory.
356            // Warning: SharedMemory dropped with active mappings
357        }
358
359        // Only free the physical pages if this object owns them
360        if state.owns_memory {
361            let num_pages = (state.capacity + PAGE_SIZE - 1) / PAGE_SIZE;
362            let pages_ptr = state.paddr as *mut crate::mem::page::Page;
363            free_raw_pages(pages_ptr, num_pages);
364            for (paddr, pages) in &state.stale_pages {
365                if *pages == 0 {
366                    continue;
367                }
368                let pages_ptr = *paddr as *mut crate::mem::page::Page;
369                free_raw_pages(pages_ptr, *pages);
370            }
371        }
372    }
373}
374
375#[cfg(test)]
376mod tests {
377    use super::*;
378    use alloc::vec::Vec;
379
380    #[test_case]
381    fn test_shared_memory_creation() {
382        // Test creating a new shared memory object
383        let permissions = 0x3; // Read + Write
384        let size = 4096;
385
386        // Test with actual allocation
387        match SharedMemory::new(size, permissions) {
388            Ok(shmem) => {
389                assert!(shmem.size() >= size); // Size might be rounded up to page size
390                assert!(shmem.is_valid());
391                assert!(shmem.supports_mmap());
392            }
393            Err(e) => {
394                // If allocation fails, that's also acceptable in test environment
395                crate::println!("SharedMemory::new failed: {}", e);
396            }
397        }
398    }
399
400    #[test_case]
401    fn test_shared_memory_from_paddr() {
402        // Test creating a shared memory object from an existing physical address
403        let paddr = 0x80000000;
404        let size = 8192;
405        let permissions = 0x3; // Read + Write
406
407        let shmem = unsafe { SharedMemory::from_paddr(paddr, size, permissions) };
408
409        assert_eq!(shmem.size(), size);
410        assert!(shmem.is_valid());
411        assert!(shmem.supports_mmap());
412    }
413
414    #[test_case]
415    fn test_shared_memory_mapping_info() {
416        let paddr = 0x80000000;
417        let size = 4096;
418        let permissions = 0x3; // Read + Write
419
420        let shmem = unsafe { SharedMemory::from_paddr(paddr, size, permissions) };
421
422        // Test valid mapping request
423        match shmem.get_mapping_info(0, 4096) {
424            Ok((mapped_paddr, mapped_perms, is_shared)) => {
425                assert_eq!(mapped_paddr, paddr);
426                assert_eq!(mapped_perms, permissions);
427                assert!(is_shared); // Shared memory should always be shared
428            }
429            Err(e) => panic!("Mapping info failed: {}", e),
430        }
431
432        // Test mapping with offset
433        match shmem.get_mapping_info(1024, 2048) {
434            Ok((mapped_paddr, mapped_perms, is_shared)) => {
435                assert_eq!(mapped_paddr, paddr + 1024);
436                assert_eq!(mapped_perms, permissions);
437                assert!(is_shared);
438            }
439            Err(e) => panic!("Mapping info with offset failed: {}", e),
440        }
441
442        // Test invalid mapping request (exceeds size)
443        assert!(shmem.get_mapping_info(0, 8192).is_err());
444        assert!(shmem.get_mapping_info(4096, 1).is_err());
445    }
446
447    #[test_case]
448    fn test_shared_memory_invalidation() {
449        let paddr = 0x80000000;
450        let size = 4096;
451        let permissions = 0x3;
452
453        let shmem = unsafe { SharedMemory::from_paddr(paddr, size, permissions) };
454
455        assert!(shmem.is_valid());
456        assert!(shmem.supports_mmap());
457
458        // Invalidate the shared memory
459        shmem.invalidate();
460
461        assert!(!shmem.is_valid());
462        assert!(!shmem.supports_mmap());
463
464        // Mapping should fail after invalidation
465        assert!(shmem.get_mapping_info(0, 4096).is_err());
466    }
467
468    #[test_case]
469    fn test_shared_memory_mapping_tracking() {
470        let paddr = 0x80000000;
471        let size = 4096;
472        let permissions = 0x3;
473
474        let shmem = unsafe { SharedMemory::from_paddr(paddr, size, permissions) };
475
476        // Initial mapping count should be 0
477        assert_eq!(shmem.state.read().mapping_count, 0);
478
479        // Simulate mapping
480        shmem.on_mapped(0x10000000, paddr, 4096, 0);
481        assert_eq!(shmem.state.read().mapping_count, 1);
482
483        // Simulate another mapping
484        shmem.on_mapped(0x20000000, paddr, 4096, 0);
485        assert_eq!(shmem.state.read().mapping_count, 2);
486
487        // Simulate unmapping
488        shmem.on_unmapped(0x10000000, 4096);
489        assert_eq!(shmem.state.read().mapping_count, 1);
490
491        shmem.on_unmapped(0x20000000, 4096);
492        assert_eq!(shmem.state.read().mapping_count, 0);
493    }
494
495    #[test_case]
496    fn test_shared_memory_mmap_owner_name() {
497        let paddr = 0x80000000;
498        let size = 4096;
499        let permissions = 0x3;
500
501        let shmem = unsafe { SharedMemory::from_paddr(paddr, size, permissions) };
502
503        let owner_name = shmem.mmap_owner_name();
504        assert!(owner_name.contains("shmem"));
505        assert!(owner_name.contains(&format!("{:#x}", paddr)));
506    }
507}