diff --git a/fs/filesystems/memfs/src/shmem.rs b/fs/filesystems/memfs/src/shmem.rs index 539c6443d7c8ca2be3ed93a12e09fa00bbe489a0..58dc9987836588ac4b6906906f909f2fe0548a2a 100644 --- a/fs/filesystems/memfs/src/shmem.rs +++ b/fs/filesystems/memfs/src/shmem.rs @@ -6,7 +6,7 @@ use alloc::{string::String, sync::Arc}; -use ksync::Mutex; +use ksync::{Mutex, static_lock}; use kvfs::{Mount, NodePermission, Path, SuperBlock, VfsFile, VfsResult, dentry_open}; use linux_raw_sys::general::{ F_SEAL_FUTURE_WRITE, F_SEAL_GROW, F_SEAL_SEAL, F_SEAL_SHRINK, F_SEAL_WRITE, O_CREAT, O_EXCL, @@ -236,6 +236,10 @@ impl ShmemObject { } } +static_lock! { + static KERNEL_SHM_FS: Mutex, Path)>> = Mutex::new(None); +} + /// Creates an anonymous tmpfs-backed file object. /// /// The returned object is not inserted into a process-visible pathname @@ -247,8 +251,23 @@ fn create_anonymous_file( permission: NodePermission, initial_seals: ShmemSealSet, ) -> VfsResult { - let fs = new_tmpfs(kvfs::StatFsFlags::empty()); - let root = Path::new(Mount::new_root(&fs), fs.root_dir()); + // Kernel-owned shm files share a single tmpfs instance to avoid + // leaking Mount/SuperBlock references on each create/destroy cycle. + let root = match kind { + ShmemObjectKind::Kernel => { + let mut guard = KERNEL_SHM_FS.lock(); + if guard.is_none() { + let fs = new_tmpfs(kvfs::StatFsFlags::empty()); + let root = Path::new(Mount::new_root(&fs), fs.root_dir()); + *guard = Some((fs, root.clone())); + } + guard.as_ref().unwrap().1.clone() + } + ShmemObjectKind::Memfd => { + let fs = new_tmpfs(kvfs::StatFsFlags::empty()); + Path::new(Mount::new_root(&fs), fs.root_dir()) + } + }; let file = kvfs::Filename::new(name).open_with_flags_at(&root, &root, O_CREAT | O_EXCL, permission)?; let location = file.path().clone(); diff --git a/mm/kalloc/src/lib.rs b/mm/kalloc/src/lib.rs index d2259132a5403f89a353fa3464d97261e20e136d..5ccf6fb3d69b4c7761b0dbcb6415e84787b30d25 100644 --- a/mm/kalloc/src/lib.rs +++ b/mm/kalloc/src/lib.rs @@ -535,6 +535,16 @@ unsafe impl GlobalAlloc for GlobalAllocator { if let Ok(ptr) = GlobalAllocator::alloc(self, layout) { ptr.as_ptr() } else { + let used = self.used_bytes(); + let avail = self.available_bytes(); + error!( + "kalloc OOM: layout {:?} (size={}, align={}), heap used={:#x} avail={:#x}", + layout, + layout.size(), + layout.align(), + used, + avail + ); alloc::alloc::handle_alloc_error(layout) } }; diff --git a/posix/ipc/src/shm.rs b/posix/ipc/src/shm.rs index fc26411be645c3a2763cdbc09b816b1e89b187cb..1d3f19dce31c198d026fe10aecdaa035b3f8eb9b 100644 --- a/posix/ipc/src/shm.rs +++ b/posix/ipc/src/shm.rs @@ -69,11 +69,19 @@ impl ShmInner { pid: Pid, ) -> KResult { let page_num = memaddr::align_up_4k(size) / PAGE_SIZE_4K; - let file = create_kernel_file( + let shm_obj = create_kernel_file( &format!("SYSV{shmid:x}"), kvfs::NodePermission::from_bits_truncate(0o600), - )? - .into_file()?; + )?; + // Unlink the backing file from tmpfs so the inode's lifetime is + // tied solely to the returned VfsFile; when the last reference + // drops the page cache is freed. + shm_obj + .location() + .mount() + .root_path() + .unlink(&shm_obj.location().name())?; + let file = shm_obj.into_file()?; file.path().truncate((page_num * PAGE_SIZE_4K) as u64)?; Ok(ShmInner { @@ -466,28 +474,37 @@ pub fn sys_shmat(shmid: i32, addr: usize, shmflg: u32) -> KResult { } pub fn sys_shmctl(shmid: i32, cmd: u32, buf: UserPtr) -> KResult { - let shm_inner = { + let shm_inner_arc = { let shm_manager = SHM_MANAGER.lock(); shm_manager .get_inner_by_shmid(shmid) .ok_or(KError::InvalidInput)? }; - let mut shm_inner = shm_inner.lock(); + let mut shm_inner = shm_inner_arc.lock(); let cmd = cmd as i32; if cmd == IPC_SET { shm_inner.shmid_ds = buf.read_vm()?; + shm_inner.shmid_ds.shm_ctime = monotonic_time_nanos() as __kernel_time_t; } else if cmd == IPC_STAT { if let Some(buf) = buf.check_non_null() { buf.write_vm(shm_inner.shmid_ds)?; } } else if cmd == IPC_RMID { shm_inner.rmid = true; + if shm_inner.attach_count() == 0 { + drop(shm_inner); + let mut shm_manager = SHM_MANAGER.lock(); + let shm_inner_recheck = shm_inner_arc.lock(); + if shm_inner_recheck.rmid && shm_inner_recheck.attach_count() == 0 { + shm_manager.remove_shmid(shmid); + } + return Ok(0); + } } else { return Err(KError::InvalidInput); } - shm_inner.shmid_ds.shm_ctime = monotonic_time_nanos() as __kernel_time_t; Ok(0) }