diff --git a/kernel/src/fs/file_table.rs b/kernel/src/fs/file_table.rs index df3237a67..99e0c9486 100644 --- a/kernel/src/fs/file_table.rs +++ b/kernel/src/fs/file_table.rs @@ -6,7 +6,7 @@ use aster_util::slot_vec::SlotVec; use super::file_handle::FileLike; use crate::{ - events::{Events, IoEvents, Observer, Subject}, + events::{IoEvents, Observer}, fs::utils::StatusFlags, prelude::*, process::{ @@ -18,16 +18,15 @@ use crate::{ pub type FileDesc = i32; +#[derive(Clone)] pub struct FileTable { table: SlotVec, - subject: Subject, } impl FileTable { pub const fn new() -> Self { Self { table: SlotVec::new(), - subject: Subject::new(), } } @@ -70,10 +69,6 @@ impl FileTable { pub fn close_file(&mut self, fd: FileDesc) -> Option> { let removed_entry = self.table.remove(fd as usize)?; - - let events = FdEvents::Close(fd); - self.notify_fd_events(&events); - Some(removed_entry.file) } @@ -100,8 +95,6 @@ impl FileTable { for fd in closed_fds { let removed_entry = self.table.remove(fd as usize).unwrap(); - let events = FdEvents::Close(fd); - self.notify_fd_events(&events); closed_files.push(removed_entry.file); } @@ -132,19 +125,6 @@ impl FileTable { .idxes_and_items() .map(|(idx, entry)| (idx as FileDesc, entry.file())) } - - pub fn register_observer(&self, observer: Weak>) { - self.subject.register_observer(observer, ()); - } - - #[expect(dead_code)] - pub fn unregister_observer(&self, observer: &Weak>) { - self.subject.unregister_observer(observer); - } - - fn notify_fd_events(&self, events: &FdEvents) { - self.subject.notify_observers(events); - } } impl Default for FileTable { @@ -153,25 +133,6 @@ impl Default for FileTable { } } -impl Clone for FileTable { - fn clone(&self) -> Self { - Self { - table: self.table.clone(), - subject: Subject::new(), - } - } -} - -impl Drop for FileTable { - fn drop(&mut self) { - // Closes all files first. - self.close_files(|_| true); - - let events = FdEvents::DropFileTable; - self.subject.notify_observers(&events); - } -} - /// A helper trait that provides methods to operate the file table. pub trait WithFileTable { /// Calls `f` with the file table. @@ -238,14 +199,6 @@ macro_rules! get_file_fast { pub(crate) use get_file_fast; -#[derive(Copy, Clone, Debug)] -pub enum FdEvents { - Close(FileDesc), - DropFileTable, -} - -impl Events for FdEvents {} - pub struct FileTableEntry { file: Arc, flags: AtomicU8, diff --git a/kernel/src/fs/procfs/mod.rs b/kernel/src/fs/procfs/mod.rs index a543eb6d6..113dd4368 100644 --- a/kernel/src/fs/procfs/mod.rs +++ b/kernel/src/fs/procfs/mod.rs @@ -2,6 +2,10 @@ use core::sync::atomic::{AtomicU64, Ordering}; +use aster_util::slot_vec::SlotVec; +use ostd::sync::RwMutexUpgradeableGuard; +use template::{lookup_child_from_table, populate_children_from_table}; + use self::{ cmdline::CmdLineFileOps, cpuinfo::CpuInfoFileOps, @@ -136,78 +140,83 @@ impl RootDirOps { root_inode } + + #[expect(clippy::type_complexity)] + const STATIC_ENTRIES: &'static [(&'static str, fn(Weak) -> Arc)] = &[ + ("cmdline", CmdLineFileOps::new_inode), + ("cpuinfo", CpuInfoFileOps::new_inode), + ("filesystems", FileSystemsFileOps::new_inode), + ("loadavg", LoadAvgFileOps::new_inode), + ("meminfo", MemInfoFileOps::new_inode), + ("self", SelfSymOps::new_inode), + ("stat", StatFileOps::new_inode), + ("sys", SysDirOps::new_inode), + ("thread-self", ThreadSelfSymOps::new_inode), + ("uptime", UptimeFileOps::new_inode), + ]; } impl Observer for ProcDir { fn on_events(&self, events: &PidEvent) { let PidEvent::Exit(pid) = events; + let mut cached_children = self.cached_children().write(); cached_children.remove_entry_by_name(&pid.to_string()); } } impl DirOps for RootDirOps { - fn lookup_child(&self, this_ptr: Weak, name: &str) -> Result> { - let child = if name == "self" { - SelfSymOps::new_inode(this_ptr.clone()) - } else if name == "sys" { - SysDirOps::new_inode(this_ptr.clone()) - } else if name == "thread-self" { - ThreadSelfSymOps::new_inode(this_ptr.clone()) - } else if name == "filesystems" { - FileSystemsFileOps::new_inode(this_ptr.clone()) - } else if name == "meminfo" { - MemInfoFileOps::new_inode(this_ptr.clone()) - } else if name == "loadavg" { - LoadAvgFileOps::new_inode(this_ptr.clone()) - } else if name == "cpuinfo" { - CpuInfoFileOps::new_inode(this_ptr.clone()) - } else if name == "uptime" { - UptimeFileOps::new_inode(this_ptr.clone()) - } else if name == "stat" { - StatFileOps::new_inode(this_ptr.clone()) - } else if name == "cmdline" { - CmdLineFileOps::new_inode(this_ptr.clone()) - } else if let Ok(pid) = name.parse::() { - let process_ref = - process_table::get_process(pid).ok_or_else(|| Error::new(Errno::ENOENT))?; - PidDirOps::new_inode(process_ref, this_ptr.clone()) - } else { - return_errno!(Errno::ENOENT); - }; - Ok(child) + // Lock order: process table -> cached entries + // + // Note that inverting the lock order is non-trivial because `Observer::on_events` will be + // called with the process table locked. + + fn lookup_child(&self, dir: &ProcDir, name: &str) -> Result> { + if let Ok(pid) = name.parse::() + && let process_table_mut = process_table::process_table_mut() + && let Some(process_ref) = process_table_mut.get(pid) + { + let mut cached_children = dir.cached_children().write(); + return Ok(cached_children + .put_entry_if_not_found(name, || { + PidDirOps::new_inode(process_ref.clone(), dir.this_weak().clone()) + }) + .clone()); + } + + let mut cached_children = dir.cached_children().write(); + + if let Some(child) = + lookup_child_from_table(name, &mut cached_children, Self::STATIC_ENTRIES, |f| { + (f)(dir.this_weak().clone()) + }) + { + return Ok(child); + } + + return_errno_with_message!(Errno::ENOENT, "the file does not exist"); } - fn populate_children(&self, this_ptr: Weak) { - let this = { - let this = this_ptr.upgrade().unwrap(); - this.downcast_ref::>().unwrap().this() - }; - let mut cached_children = this.cached_children().write(); - cached_children.put_entry_if_not_found("self", || SelfSymOps::new_inode(this_ptr.clone())); - cached_children.put_entry_if_not_found("thread-self", || { - ThreadSelfSymOps::new_inode(this_ptr.clone()) - }); - cached_children.put_entry_if_not_found("sys", || SysDirOps::new_inode(this_ptr.clone())); - cached_children.put_entry_if_not_found("filesystems", || { - FileSystemsFileOps::new_inode(this_ptr.clone()) - }); - cached_children - .put_entry_if_not_found("meminfo", || MemInfoFileOps::new_inode(this_ptr.clone())); - cached_children - .put_entry_if_not_found("loadavg", || LoadAvgFileOps::new_inode(this_ptr.clone())); - cached_children - .put_entry_if_not_found("cpuinfo", || CpuInfoFileOps::new_inode(this_ptr.clone())); - cached_children - .put_entry_if_not_found("uptime", || UptimeFileOps::new_inode(this_ptr.clone())); - cached_children.put_entry_if_not_found("stat", || StatFileOps::new_inode(this_ptr.clone())); - cached_children - .put_entry_if_not_found("cmdline", || CmdLineFileOps::new_inode(this_ptr.clone())); - for process in process_table::process_table_mut().iter() { - let pid = process.pid().to_string(); + fn populate_children<'a>( + &self, + dir: &'a ProcDir, + ) -> RwMutexUpgradeableGuard<'a, SlotVec<(String, Arc)>> { + let process_table_mut = process_table::process_table_mut(); + let mut cached_children = dir.cached_children().write(); + + for process_ref in process_table_mut.iter() { + let pid = process_ref.pid().to_string(); cached_children.put_entry_if_not_found(&pid, || { - PidDirOps::new_inode(process.clone(), this_ptr.clone()) + PidDirOps::new_inode(process_ref.clone(), dir.this_weak().clone()) }); } + + drop(process_table_mut); + + populate_children_from_table(&mut cached_children, Self::STATIC_ENTRIES, |f| { + (f)(dir.this_weak().clone()) + }); + + cached_children.downgrade() } } diff --git a/kernel/src/fs/procfs/pid/mod.rs b/kernel/src/fs/procfs/pid/mod.rs index f931e9c46..af9b390dd 100644 --- a/kernel/src/fs/procfs/pid/mod.rs +++ b/kernel/src/fs/procfs/pid/mod.rs @@ -1,18 +1,21 @@ // SPDX-License-Identifier: MPL-2.0 -use super::template::{DirOps, ProcDir, ProcDirBuilder}; +use aster_util::slot_vec::SlotVec; +use ostd::sync::RwMutexUpgradeableGuard; + +use super::template::{ + lookup_child_from_table, populate_children_from_table, DirOps, ProcDir, ProcDirBuilder, +}; use crate::{ - events::Observer, fs::{ - file_table::FdEvents, procfs::pid::{ stat::StatFileOps, task::{TaskDirOps, TidDirOps}, }, - utils::{mkmod, DirEntryVecExt, Inode}, + utils::{mkmod, Inode}, }, prelude::*, - process::{posix_thread::AsPosixThread, Process}, + process::Process, }; mod stat; @@ -35,88 +38,63 @@ impl PidDirOps { thread_ref, } }; - let file_table = tid_dir_ops - .thread_ref - .as_posix_thread() - .unwrap() - .file_table(); - // Reference: - let pid_inode = ProcDirBuilder::new(Self(tid_dir_ops.clone()), mkmod!(a+rx)) + ProcDirBuilder::new(Self(tid_dir_ops.clone()), mkmod!(a+rx)) .parent(parent) - // The pid directories must be volatile, because it is just associated with one process. + // The PID directories must be volatile, because it is just associated with one process. .volatile() .build() - .unwrap(); - - // This is for an exiting process that has not yet been reaped by its parent, - // whose file table may have already been released. - if let Some(file_table_ref) = file_table.lock().as_ref() { - file_table_ref - .read() - .register_observer(Arc::downgrade(&pid_inode) as _); - } - - pid_inode + .unwrap() } -} -impl Observer for ProcDir { - fn on_events(&self, events: &FdEvents) { - if let FdEvents::DropFileTable = events { - let mut cached_children = self.cached_children().write(); - cached_children.remove_entry_by_name("fd"); - } - } + #[expect(clippy::type_complexity)] + const STATIC_ENTRIES: &'static [( + &'static str, + fn(&PidDirOps, Weak) -> Arc, + )] = &[ + ("stat", StatFileOps::new_inode_pid), + ("task", TaskDirOps::new_inode), + ]; } impl DirOps for PidDirOps { - fn lookup_child(&self, this_ptr: Weak, name: &str) -> Result> { + fn lookup_child(&self, dir: &ProcDir, name: &str) -> Result> { + let mut cached_children = dir.cached_children().write(); + // Look up entries that either exist under `/proc/` // but not under `/proc//task/`, // or entries whose contents differ between `/proc/` and `/proc//task/`. - match name { - "stat" => { - return Ok(StatFileOps::new_inode( - self.0.process_ref.clone(), - self.0.thread_ref.clone(), - true, - this_ptr, - )) - } - "task" => return Ok(TaskDirOps::new_inode(self.0.process_ref.clone(), this_ptr)), - _ => {} + if let Some(child) = + lookup_child_from_table(name, &mut cached_children, Self::STATIC_ENTRIES, |f| { + (f)(self, dir.this_weak().clone()) + }) + { + return Ok(child); } // For all other children, the content is the same under both `/proc/` and `/proc//task/`. - self.0.lookup_child(this_ptr, name) + self.0 + .lookup_child_locked(&mut cached_children, dir.this_weak().clone(), name) } - fn populate_children(&self, this_ptr: Weak) { - let this = { - let this = this_ptr.upgrade().unwrap(); - this.downcast_ref::>().unwrap().this() - }; - let mut cached_children = this.cached_children().write(); + fn populate_children<'a>( + &self, + dir: &'a ProcDir, + ) -> RwMutexUpgradeableGuard<'a, SlotVec<(String, Arc)>> { + let mut cached_children = dir.cached_children().write(); // Populate entries that either exist under `/proc/` // but not under `/proc//task/`, // or whose contents differ between the two paths. - cached_children.put_entry_if_not_found("stat", || { - StatFileOps::new_inode( - self.0.process_ref.clone(), - self.0.thread_ref.clone(), - true, - this_ptr.clone(), - ) - }); - cached_children.put_entry_if_not_found("task", || { - TaskDirOps::new_inode(self.0.process_ref.clone(), this_ptr.clone()) + populate_children_from_table(&mut cached_children, Self::STATIC_ENTRIES, |f| { + (f)(self, dir.this_weak().clone()) }); // Populate the remaining children that are identical // under both `/proc/` and `/proc//task/`. self.0 - .populate_children_inner(&mut cached_children, this_ptr); + .populate_children_locked(&mut cached_children, dir.this_weak().clone()); + + cached_children.downgrade() } } diff --git a/kernel/src/fs/procfs/pid/stat.rs b/kernel/src/fs/procfs/pid/stat.rs index cc89faab4..9d061de62 100644 --- a/kernel/src/fs/procfs/pid/stat.rs +++ b/kernel/src/fs/procfs/pid/stat.rs @@ -2,6 +2,7 @@ use core::{fmt::Write, sync::atomic::Ordering}; +use super::{PidDirOps, TidDirOps}; use crate::{ fs::{ procfs::template::{FileOps, ProcFileBuilder}, @@ -81,7 +82,19 @@ pub struct StatFileOps { } impl StatFileOps { - pub fn new_inode( + pub fn new_inode_pid(dir: &PidDirOps, parent: Weak) -> Arc { + let process_ref = dir.0.process_ref.clone(); + let thread_ref = dir.0.thread_ref.clone(); + Self::new_inode_impl(process_ref, thread_ref, true, parent) + } + + pub fn new_inode_tid(dir: &TidDirOps, parent: Weak) -> Arc { + let process_ref = dir.process_ref.clone(); + let thread_ref = dir.thread_ref.clone(); + Self::new_inode_impl(process_ref, thread_ref, false, parent) + } + + fn new_inode_impl( process_ref: Arc, thread_ref: Arc, is_pid_stat: bool, diff --git a/kernel/src/fs/procfs/pid/task/cmdline.rs b/kernel/src/fs/procfs/pid/task/cmdline.rs index 0c159ff23..707a012aa 100644 --- a/kernel/src/fs/procfs/pid/task/cmdline.rs +++ b/kernel/src/fs/procfs/pid/task/cmdline.rs @@ -1,5 +1,6 @@ // SPDX-License-Identifier: MPL-2.0 +use super::TidDirOps; use crate::{ fs::{ procfs::template::{FileOps, ProcFileBuilder}, @@ -13,7 +14,8 @@ use crate::{ pub struct CmdlineFileOps(Arc); impl CmdlineFileOps { - pub fn new_inode(process_ref: Arc, parent: Weak) -> Arc { + pub fn new_inode(dir: &TidDirOps, parent: Weak) -> Arc { + let process_ref = dir.process_ref.clone(); // Reference: ProcFileBuilder::new(Self(process_ref), mkmod!(a+r)) .parent(parent) diff --git a/kernel/src/fs/procfs/pid/task/comm.rs b/kernel/src/fs/procfs/pid/task/comm.rs index b5eaaa464..877904f8c 100644 --- a/kernel/src/fs/procfs/pid/task/comm.rs +++ b/kernel/src/fs/procfs/pid/task/comm.rs @@ -1,5 +1,6 @@ // SPDX-License-Identifier: MPL-2.0 +use super::TidDirOps; use crate::{ fs::{ procfs::template::{FileOps, ProcFileBuilder}, @@ -13,7 +14,8 @@ use crate::{ pub struct CommFileOps(Arc); impl CommFileOps { - pub fn new_inode(process_ref: Arc, parent: Weak) -> Arc { + pub fn new_inode(dir: &TidDirOps, parent: Weak) -> Arc { + let process_ref = dir.process_ref.clone(); // Reference: ProcFileBuilder::new(Self(process_ref), mkmod!(a+r, u+w)) .parent(parent) diff --git a/kernel/src/fs/procfs/pid/task/environ.rs b/kernel/src/fs/procfs/pid/task/environ.rs index 24d905ebc..7fb29dc56 100644 --- a/kernel/src/fs/procfs/pid/task/environ.rs +++ b/kernel/src/fs/procfs/pid/task/environ.rs @@ -1,5 +1,6 @@ // SPDX-License-Identifier: MPL-2.0 +use super::TidDirOps; use crate::{ fs::{ procfs::template::{FileOps, ProcFileBuilder}, @@ -13,7 +14,8 @@ use crate::{ pub struct EnvironFileOps(Arc); impl EnvironFileOps { - pub fn new_inode(process_ref: Arc, parent: Weak) -> Arc { + pub fn new_inode(dir: &TidDirOps, parent: Weak) -> Arc { + let process_ref = dir.process_ref.clone(); // Reference: ProcFileBuilder::new(Self(process_ref), mkmod!(u+r)) .parent(parent) diff --git a/kernel/src/fs/procfs/pid/task/exe.rs b/kernel/src/fs/procfs/pid/task/exe.rs index 6ed654685..5f1424617 100644 --- a/kernel/src/fs/procfs/pid/task/exe.rs +++ b/kernel/src/fs/procfs/pid/task/exe.rs @@ -1,5 +1,6 @@ // SPDX-License-Identifier: MPL-2.0 +use super::TidDirOps; use crate::{ fs::{ procfs::{ProcSymBuilder, SymOps}, @@ -13,7 +14,8 @@ use crate::{ pub struct ExeSymOps(Arc); impl ExeSymOps { - pub fn new_inode(process_ref: Arc, parent: Weak) -> Arc { + pub fn new_inode(dir: &TidDirOps, parent: Weak) -> Arc { + let process_ref = dir.process_ref.clone(); // Reference: // // diff --git a/kernel/src/fs/procfs/pid/task/fd.rs b/kernel/src/fs/procfs/pid/task/fd.rs index 1672828c9..649e36fe1 100644 --- a/kernel/src/fs/procfs/pid/task/fd.rs +++ b/kernel/src/fs/procfs/pid/task/fd.rs @@ -1,14 +1,15 @@ // SPDX-License-Identifier: MPL-2.0 +use aster_util::slot_vec::SlotVec; +use ostd::sync::RwMutexUpgradeableGuard; + +use super::TidDirOps; use crate::{ fs::{ - file_handle::FileLike, file_table::FileDesc, inode_handle::InodeHandle, - procfs::{ - pid::FdEvents, DirOps, Observer, ProcDir, ProcDirBuilder, ProcSymBuilder, SymOps, - }, - utils::{chmod, mkmod, DirEntryVecExt, Inode}, + procfs::{template::ProcSym, DirOps, ProcDir, ProcDirBuilder, ProcSymBuilder, SymOps}, + utils::{chmod, mkmod, AccessMode, DirEntryVecExt, Inode}, }, prelude::*, process::posix_thread::AsPosixThread, @@ -19,115 +20,178 @@ use crate::{ pub struct FdDirOps(Arc); impl FdDirOps { - pub fn new_inode(thread_ref: Arc, parent: Weak) -> Arc { - let posix_thread = thread_ref.as_posix_thread().unwrap(); - let file_table = posix_thread.file_table(); - - let fd_inode = ProcDirBuilder::new( - Self(thread_ref.clone()), - // Reference: - mkmod!(u+rx), - ) - .parent(parent) - .build() - .unwrap(); - // This is for an exiting process that has not yet been reaped by its parent, - // whose file table may have already been released. - if let Some(file_table_ref) = file_table.lock().as_ref() { - file_table_ref - .read() - .register_observer(Arc::downgrade(&fd_inode) as _); - } - - fd_inode - } -} - -impl Observer for ProcDir { - fn on_events(&self, events: &FdEvents) { - let fd_string = if let FdEvents::Close(fd) = events { - fd.to_string() - } else { - return; - }; - - let mut cached_children = self.cached_children().write(); - cached_children.remove_entry_by_name(&fd_string); - } -} - -impl DirOps for FdDirOps { - fn lookup_child(&self, this_ptr: Weak, name: &str) -> Result> { - let posix_thread = self.0.as_posix_thread().unwrap(); - let file_table = posix_thread.file_table().lock(); - let file_table = file_table - .as_ref() - .ok_or_else(|| Error::new(Errno::ENOENT))?; - - let file = { - let fd = name - .parse::() - .map_err(|_| Error::new(Errno::ENOENT))?; - file_table - .read() - .get_file(fd) - .map_err(|_| Error::new(Errno::ENOENT))? - .clone() - }; - - Ok(FileSymOps::new_inode(file, this_ptr.clone())) - } - - fn populate_children(&self, this_ptr: Weak) { - let posix_thread = self.0.as_posix_thread().unwrap(); - let file_table = posix_thread.file_table().lock(); - let Some(file_table) = file_table.as_ref() else { - return; - }; - - let this = { - let this = this_ptr.upgrade().unwrap(); - this.downcast_ref::>().unwrap().this() - }; - let mut cached_children = this.cached_children().write(); - - for (fd, file) in file_table.read().fds_and_files() { - cached_children.put_entry_if_not_found(&fd.to_string(), || { - FileSymOps::new_inode(file.clone(), this_ptr.clone()) - }); - } - } -} - -/// Represents the inode at `/proc/[pid]/task/[tid]/fd/[n]` (and also `/proc/[pid]/fd/[n]`). -struct FileSymOps(Arc); - -impl FileSymOps { - pub fn new_inode(file: Arc, parent: Weak) -> Arc { - // Reference: - let mut mode = mkmod!(a=); - if file.access_mode().is_readable() { - mode = chmod!(mode, u+rx); - } - if file.access_mode().is_writable() { - mode = chmod!(mode, u+wx); - } - - ProcSymBuilder::new(Self(file), mode) + pub fn new_inode(dir: &TidDirOps, parent: Weak) -> Arc { + let thread_ref = dir.thread_ref.clone(); + // Reference: + ProcDirBuilder::new(Self(thread_ref), mkmod!(u+rx)) .parent(parent) .build() .unwrap() } } +impl DirOps for FdDirOps { + // Lock order: cached entries -> file table + // + // Note that inverting the lock order is non-trivial because the file table is protected by a + // spin lock but the cached entries are protected by a mutex. + + fn lookup_child(&self, dir: &ProcDir, name: &str) -> Result> { + let Ok(file_desc) = name.parse::() else { + return_errno_with_message!(Errno::ENOENT, "the name is not a valid FD"); + }; + + let mut cached_children = dir.cached_children().write(); + + let posix_thread = self.0.as_posix_thread().unwrap(); + let access_mode = if let Some(file_table) = posix_thread.file_table().lock().as_ref() + && let Ok(file) = file_table.read().get_file(file_desc) + { + file.access_mode() + } else { + return_errno_with_message!(Errno::ENOENT, "the file does not exist"); + }; + + let child = FileSymOps::new_inode( + self.0.clone(), + file_desc, + access_mode, + dir.this_weak().clone(), + ); + // The old entry is likely outdated given that `lookup_child` is called. Race conditions + // may occur, but caching the file descriptor (which aligns with the Linux implementation) + // is inherently racy, so preventing race conditions is not very meaningful. + cached_children.remove_entry_by_name(name); + cached_children.put((String::from(name), child.clone())); + + Ok(child) + } + + fn populate_children<'a>( + &self, + dir: &'a ProcDir, + ) -> RwMutexUpgradeableGuard<'a, SlotVec<(String, Arc)>> { + let mut cached_children = dir.cached_children().write(); + + let posix_thread = self.0.as_posix_thread().unwrap(); + let file_table = posix_thread.file_table().lock(); + let Some(file_table) = file_table.as_ref() else { + *cached_children = SlotVec::new(); + return cached_children.downgrade(); + }; + + let file_table = file_table.read(); + + // Remove outdated entries. + for i in 0..cached_children.slots_len() { + let Some((_, child)) = cached_children.get(i) else { + continue; + }; + let child = child.downcast_ref::>().unwrap(); + + let Ok(file) = file_table.get_file(child.inner().file_desc) else { + cached_children.remove(i); + continue; + }; + if file.access_mode() != child.inner().access_mode { + cached_children.remove(i); + } + // We'll reuse the old entry if the access mode is the same, even if the file is + // different. + } + + // Add new entries. + for (file_desc, file) in file_table.fds_and_files() { + cached_children.put_entry_if_not_found(&file_desc.to_string(), || { + FileSymOps::new_inode( + self.0.clone(), + file_desc, + file.access_mode(), + dir.this_weak().clone(), + ) + }); + } + + cached_children.downgrade() + } + + fn validate_child(&self, child: &dyn Inode) -> bool { + let ops = child.downcast_ref::>().unwrap(); + + let posix_thread = self.0.as_posix_thread().unwrap(); + let is_valid = if let Some(file_table) = posix_thread.file_table().lock().as_ref() + && let Ok(file) = file_table.read().get_file(ops.inner().file_desc) + { + // We'll reuse the old entry if the access mode is the same, even if the file is + // different. + file.access_mode() == ops.inner().access_mode + } else { + false + }; + + is_valid + } +} + +/// Represents the inode at `/proc/[pid]/task/[tid]/fd/[n]` (and also `/proc/[pid]/fd/[n]`). +struct FileSymOps { + thread_ref: Arc, + file_desc: FileDesc, + access_mode: AccessMode, +} + +impl FileSymOps { + pub fn new_inode( + thread_ref: Arc, + file_desc: FileDesc, + access_mode: AccessMode, + parent: Weak, + ) -> Arc { + // Reference: + let mut mode = mkmod!(a=); + if access_mode.is_readable() { + mode = chmod!(mode, u+rx); + } + if access_mode.is_writable() { + mode = chmod!(mode, u+wx); + } + + ProcSymBuilder::new( + Self { + thread_ref, + file_desc, + access_mode, + }, + mode, + ) + .parent(parent) + .build() + .unwrap() + } +} + impl SymOps for FileSymOps { fn read_link(&self) -> Result { - let path_name = if let Some(inode_handle) = self.0.downcast_ref::() { + let posix_thread = self.thread_ref.as_posix_thread().unwrap(); + + let file = if let Some(file_table) = posix_thread.file_table().lock().as_ref() + && let Ok(file) = file_table.read().get_file(self.file_desc) + { + file.clone() + } else { + return_errno_with_message!(Errno::ENOENT, "the file does not exist"); + }; + + let path_name = if let Some(inode_handle) = file.downcast_ref::() { inode_handle.path().abs_path() } else { - // TODO: get the real path for other FileLike object + // TODO: Get the real path of other `FileLike` objects. String::from("/dev/tty") }; + + // FIXME: This may not always be a suitable context to drop a `FileLike` object. + drop(file); + Ok(path_name) } } diff --git a/kernel/src/fs/procfs/pid/task/gid_map.rs b/kernel/src/fs/procfs/pid/task/gid_map.rs index a3be564a6..708cabfe5 100644 --- a/kernel/src/fs/procfs/pid/task/gid_map.rs +++ b/kernel/src/fs/procfs/pid/task/gid_map.rs @@ -2,6 +2,7 @@ use alloc::format; +use super::TidDirOps; use crate::{ fs::{ procfs::template::{FileOps, ProcFileBuilder}, @@ -16,8 +17,9 @@ use crate::{ pub struct GidMapFileOps(Arc); impl GidMapFileOps { - pub fn new_inode(process_ref: Arc, parent: Weak) -> Arc { - // Reference: + pub fn new_inode(dir: &TidDirOps, parent: Weak) -> Arc { + let process_ref = dir.process_ref.clone(); + // Reference: ProcFileBuilder::new(Self(process_ref), mkmod!(a+r, u+w)) .parent(parent) .build() diff --git a/kernel/src/fs/procfs/pid/task/mem.rs b/kernel/src/fs/procfs/pid/task/mem.rs index 099145aec..8e857c4a8 100644 --- a/kernel/src/fs/procfs/pid/task/mem.rs +++ b/kernel/src/fs/procfs/pid/task/mem.rs @@ -1,5 +1,6 @@ // SPDX-License-Identifier: MPL-2.0 +use super::TidDirOps; use crate::{ fs::{ procfs::template::{FileOps, ProcFileBuilder}, @@ -13,7 +14,8 @@ use crate::{ pub struct MemFileOps(Arc); impl MemFileOps { - pub fn new_inode(process_ref: Arc, parent: Weak) -> Arc { + pub fn new_inode(dir: &TidDirOps, parent: Weak) -> Arc { + let process_ref = dir.process_ref.clone(); // Reference: ProcFileBuilder::new(Self(process_ref), mkmod!(u+rw)) .parent(parent) diff --git a/kernel/src/fs/procfs/pid/task/mod.rs b/kernel/src/fs/procfs/pid/task/mod.rs index d2f0c38aa..379baa5df 100644 --- a/kernel/src/fs/procfs/pid/task/mod.rs +++ b/kernel/src/fs/procfs/pid/task/mod.rs @@ -1,9 +1,9 @@ // SPDX-License-Identifier: MPL-2.0 -use alloc::format; - use aster_util::slot_vec::SlotVec; +use ostd::sync::RwMutexUpgradeableGuard; +use super::PidDirOps; use crate::{ fs::{ procfs::{ @@ -16,13 +16,16 @@ use crate::{ status::StatusFileOps, uid_map::UidMapFileOps, }, }, - template::{DirOps, ProcDir, ProcDirBuilder}, + template::{ + lookup_child_from_table, populate_children_from_table, DirOps, ProcDir, + ProcDirBuilder, + }, }, utils::{mkmod, DirEntryVecExt, Inode}, }, prelude::*, process::posix_thread::AsPosixThread, - thread::{AsThread, Thread}, + thread::{AsThread, Thread, Tid}, Process, }; @@ -42,7 +45,8 @@ mod uid_map; pub struct TaskDirOps(Arc); impl TaskDirOps { - pub fn new_inode(process_ref: Arc, parent: Weak) -> Arc { + pub fn new_inode(dir: &PidDirOps, parent: Weak) -> Arc { + let process_ref = dir.0.process_ref.clone(); // Reference: ProcDirBuilder::new(Self(process_ref), mkmod!(a+rx)) .parent(parent) @@ -76,133 +80,120 @@ impl TidDirOps { .build() .unwrap() } + + #[expect(clippy::type_complexity)] + const STATIC_ENTRIES: &'static [( + &'static str, + fn(&TidDirOps, Weak) -> Arc, + )] = &[ + ("cmdline", CmdlineFileOps::new_inode), + ("comm", CommFileOps::new_inode), + ("environ", EnvironFileOps::new_inode), + ("exe", ExeSymOps::new_inode), + ("fd", FdDirOps::new_inode), + ("gid_map", GidMapFileOps::new_inode), + ("mem", MemFileOps::new_inode), + ("mountinfo", MountInfoFileOps::new_inode), + ("oom_score_adj", OomScoreAdjFileOps::new_inode), + ("stat", StatFileOps::new_inode_tid), + ("status", StatusFileOps::new_inode), + ("uid_map", UidMapFileOps::new_inode), + ]; } impl DirOps for TidDirOps { - fn lookup_child(&self, this_ptr: Weak, name: &str) -> Result> { - let inode = match name { - "cmdline" => CmdlineFileOps::new_inode(self.process_ref.clone(), this_ptr), - "comm" => CommFileOps::new_inode(self.process_ref.clone(), this_ptr), - "environ" => EnvironFileOps::new_inode(self.process_ref.clone(), this_ptr), - "exe" => ExeSymOps::new_inode(self.process_ref.clone(), this_ptr), - "fd" => FdDirOps::new_inode(self.thread_ref.clone(), this_ptr), - "gid_map" => GidMapFileOps::new_inode(self.process_ref.clone(), this_ptr), - "mem" => MemFileOps::new_inode(self.process_ref.clone(), this_ptr), - "mountinfo" => MountInfoFileOps::new_inode(self.thread_ref.clone(), this_ptr), - "oom_score_adj" => OomScoreAdjFileOps::new_inode(self.process_ref.clone(), this_ptr), - "stat" => StatFileOps::new_inode( - self.process_ref.clone(), - self.thread_ref.clone(), - false, - this_ptr, - ), - "status" => StatusFileOps::new_inode( - self.process_ref.clone(), - self.thread_ref.clone(), - this_ptr, - ), - "uid_map" => UidMapFileOps::new_inode(self.process_ref.clone(), this_ptr), - _ => return_errno!(Errno::ENOENT), - }; - Ok(inode) + fn lookup_child(&self, dir: &ProcDir, name: &str) -> Result> { + let mut cached_children = dir.cached_children().write(); + self.lookup_child_locked(&mut cached_children, dir.this_weak().clone(), name) } - fn populate_children(&self, this_ptr: Weak) { - let this = { - let this = this_ptr.upgrade().unwrap(); - this.downcast_ref::>().unwrap().this() - }; - let mut cached_children = this.cached_children().write(); - self.populate_children_inner(&mut cached_children, this_ptr); + fn populate_children<'a>( + &self, + dir: &'a ProcDir, + ) -> RwMutexUpgradeableGuard<'a, SlotVec<(String, Arc)>> { + let mut cached_children = dir.cached_children().write(); + self.populate_children_locked(&mut cached_children, dir.this_weak().clone()); + cached_children.downgrade() } } impl TidDirOps { - pub(super) fn populate_children_inner( + pub(super) fn lookup_child_locked( + &self, + cached_children: &mut SlotVec<(String, Arc)>, + this_ptr: Weak, + name: &str, + ) -> Result> { + if let Some(child) = + lookup_child_from_table(name, cached_children, Self::STATIC_ENTRIES, |f| { + (f)(self, this_ptr) + }) + { + return Ok(child); + } + + return_errno_with_message!(Errno::ENOENT, "the file does not exist"); + } + + pub(super) fn populate_children_locked( &self, cached_children: &mut SlotVec<(String, Arc)>, this_ptr: Weak, ) { - cached_children.put_entry_if_not_found("cmdline", || { - CmdlineFileOps::new_inode(self.process_ref.clone(), this_ptr.clone()) - }); - cached_children.put_entry_if_not_found("comm", || { - CommFileOps::new_inode(self.process_ref.clone(), this_ptr.clone()) - }); - cached_children.put_entry_if_not_found("environ", || { - EnvironFileOps::new_inode(self.process_ref.clone(), this_ptr.clone()) - }); - cached_children.put_entry_if_not_found("exe", || { - ExeSymOps::new_inode(self.process_ref.clone(), this_ptr.clone()) - }); - cached_children.put_entry_if_not_found("fd", || { - FdDirOps::new_inode(self.thread_ref.clone(), this_ptr.clone()) - }); - cached_children.put_entry_if_not_found("gid_map", || { - GidMapFileOps::new_inode(self.process_ref.clone(), this_ptr.clone()) - }); - cached_children.put_entry_if_not_found("mem", || { - MemFileOps::new_inode(self.process_ref.clone(), this_ptr.clone()) - }); - cached_children.put_entry_if_not_found("mountinfo", || { - MountInfoFileOps::new_inode(self.thread_ref.clone(), this_ptr.clone()) - }); - cached_children.put_entry_if_not_found("oom_score_adj", || { - OomScoreAdjFileOps::new_inode(self.process_ref.clone(), this_ptr.clone()) - }); - cached_children.put_entry_if_not_found("stat", || { - StatFileOps::new_inode( - self.process_ref.clone(), - self.thread_ref.clone(), - false, - this_ptr.clone(), - ) - }); - cached_children.put_entry_if_not_found("status", || { - StatusFileOps::new_inode( - self.process_ref.clone(), - self.thread_ref.clone(), - this_ptr.clone(), - ) - }); - cached_children.put_entry_if_not_found("uid_map", || { - UidMapFileOps::new_inode(self.process_ref.clone(), this_ptr.clone()) + populate_children_from_table(cached_children, Self::STATIC_ENTRIES, |f| { + (f)(self, this_ptr.clone()) }); } } impl DirOps for TaskDirOps { - fn lookup_child(&self, this_ptr: Weak, name: &str) -> Result> { - let Ok(tid) = name.parse::() else { - return_errno_with_message!(Errno::ENOENT, "Can not parse name to u32 type"); + fn lookup_child(&self, dir: &ProcDir, name: &str) -> Result> { + let Ok(tid) = name.parse::() else { + return_errno_with_message!(Errno::ENOENT, "the name is not a valid TID"); }; for task in self.0.tasks().lock().as_slice() { - let thread = task.as_thread().unwrap(); - if thread.as_posix_thread().unwrap().tid() != tid { + let thread_ref = task.as_thread().unwrap(); + if thread_ref.as_posix_thread().unwrap().tid() != tid { continue; } - return Ok(TidDirOps::new_inode( - self.0.clone(), - thread.clone(), - this_ptr, - )); + + let mut cached_children = dir.cached_children().write(); + return Ok(cached_children + .put_entry_if_not_found(name, || { + TidDirOps::new_inode( + self.0.clone(), + thread_ref.clone(), + dir.this_weak().clone(), + ) + }) + .clone()); } - return_errno_with_message!(Errno::ENOENT, "No such thread") + + return_errno_with_message!(Errno::ENOENT, "the thread does not exist") } - fn populate_children(&self, this_ptr: Weak) { - let this = { - let this = this_ptr.upgrade().unwrap(); - this.downcast_ref::>().unwrap().this() - }; - let mut cached_children = this.cached_children().write(); - for task in self.0.tasks().lock().as_slice() { - let thread = task.as_thread().unwrap(); - cached_children.put_entry_if_not_found( - &format!("{}", task.as_posix_thread().unwrap().tid()), - || TidDirOps::new_inode(self.0.clone(), thread.clone(), this_ptr.clone()), + fn populate_children<'a>( + &self, + dir: &'a ProcDir, + ) -> RwMutexUpgradeableGuard<'a, SlotVec<(String, Arc)>> { + let tasks = self.0.tasks().lock(); + let mut cached_dentries = dir.cached_children().write(); + + for task in tasks.as_slice() { + let thread_ref = task.as_thread().unwrap(); + cached_dentries.put_entry_if_not_found( + &task.as_posix_thread().unwrap().tid().to_string(), + || { + TidDirOps::new_inode( + self.0.clone(), + thread_ref.clone(), + dir.this_weak().clone(), + ) + }, ); } + + cached_dentries.downgrade() } } diff --git a/kernel/src/fs/procfs/pid/task/mountinfo.rs b/kernel/src/fs/procfs/pid/task/mountinfo.rs index 7c7f66172..0d2da7940 100644 --- a/kernel/src/fs/procfs/pid/task/mountinfo.rs +++ b/kernel/src/fs/procfs/pid/task/mountinfo.rs @@ -1,5 +1,6 @@ // SPDX-License-Identifier: MPL-2.0 +use super::TidDirOps; use crate::{ fs::{ procfs::template::{FileOps, ProcFileBuilder}, @@ -16,7 +17,8 @@ pub struct MountInfoFileOps { } impl MountInfoFileOps { - pub fn new_inode(thread_ref: Arc, parent: Weak) -> Arc { + pub fn new_inode(dir: &TidDirOps, parent: Weak) -> Arc { + let thread_ref = dir.thread_ref.clone(); // Reference: ProcFileBuilder::new(Self { thread_ref }, mkmod!(a+r)) .parent(parent) diff --git a/kernel/src/fs/procfs/pid/task/oom_score_adj.rs b/kernel/src/fs/procfs/pid/task/oom_score_adj.rs index fa33ea7d1..1fef7d25f 100644 --- a/kernel/src/fs/procfs/pid/task/oom_score_adj.rs +++ b/kernel/src/fs/procfs/pid/task/oom_score_adj.rs @@ -2,6 +2,7 @@ use core::{fmt::Write, sync::atomic::Ordering}; +use super::TidDirOps; use crate::{ fs::{ procfs::template::{FileOps, ProcFileBuilder}, @@ -15,7 +16,8 @@ use crate::{ pub struct OomScoreAdjFileOps(Arc); impl OomScoreAdjFileOps { - pub fn new_inode(process_ref: Arc, parent: Weak) -> Arc { + pub fn new_inode(dir: &TidDirOps, parent: Weak) -> Arc { + let process_ref = dir.process_ref.clone(); // Reference: ProcFileBuilder::new(Self(process_ref), mkmod!(a+r, u+w)) .parent(parent) diff --git a/kernel/src/fs/procfs/pid/task/status.rs b/kernel/src/fs/procfs/pid/task/status.rs index fc713cbcf..0312a7549 100644 --- a/kernel/src/fs/procfs/pid/task/status.rs +++ b/kernel/src/fs/procfs/pid/task/status.rs @@ -2,6 +2,7 @@ use core::fmt::Write; +use super::TidDirOps; use crate::{ fs::{ procfs::template::{FileOps, ProcFileBuilder}, @@ -65,11 +66,9 @@ pub struct StatusFileOps { } impl StatusFileOps { - pub fn new_inode( - process_ref: Arc, - thread_ref: Arc, - parent: Weak, - ) -> Arc { + pub fn new_inode(dir: &TidDirOps, parent: Weak) -> Arc { + let process_ref = dir.process_ref.clone(); + let thread_ref = dir.thread_ref.clone(); ProcFileBuilder::new( Self { process_ref, diff --git a/kernel/src/fs/procfs/pid/task/uid_map.rs b/kernel/src/fs/procfs/pid/task/uid_map.rs index 48ac8d6f5..efa110451 100644 --- a/kernel/src/fs/procfs/pid/task/uid_map.rs +++ b/kernel/src/fs/procfs/pid/task/uid_map.rs @@ -2,6 +2,7 @@ use alloc::format; +use super::TidDirOps; use crate::{ fs::{ procfs::template::{FileOps, ProcFileBuilder}, @@ -16,8 +17,9 @@ use crate::{ pub struct UidMapFileOps(Arc); impl UidMapFileOps { - pub fn new_inode(process_ref: Arc, parent: Weak) -> Arc { - // Reference: + pub fn new_inode(dir: &TidDirOps, parent: Weak) -> Arc { + let process_ref = dir.process_ref.clone(); + // Reference: ProcFileBuilder::new(Self(process_ref), mkmod!(a+r, u+w)) .parent(parent) .build() diff --git a/kernel/src/fs/procfs/sys/kernel/mod.rs b/kernel/src/fs/procfs/sys/kernel/mod.rs index 9784e6399..fad001066 100644 --- a/kernel/src/fs/procfs/sys/kernel/mod.rs +++ b/kernel/src/fs/procfs/sys/kernel/mod.rs @@ -1,13 +1,18 @@ // SPDX-License-Identifier: MPL-2.0 +use aster_util::slot_vec::SlotVec; +use ostd::sync::RwMutexUpgradeableGuard; + use crate::{ fs::{ procfs::{ sys::kernel::{cap_last_cap::CapLastCapFileOps, pid_max::PidMaxFileOps}, - template::{DirOps, ProcDirBuilder}, + template::{ + lookup_child_from_table, populate_children_from_table, DirOps, ProcDirBuilder, + }, ProcDir, }, - utils::{mkmod, DirEntryVecExt, Inode}, + utils::{mkmod, Inode}, }, prelude::*, }; @@ -28,28 +33,39 @@ impl KernelDirOps { .build() .unwrap() } + + #[expect(clippy::type_complexity)] + const STATIC_ENTRIES: &'static [(&'static str, fn(Weak) -> Arc)] = &[ + ("cap_last_cap", CapLastCapFileOps::new_inode), + ("pid_max", PidMaxFileOps::new_inode), + ]; } impl DirOps for KernelDirOps { - fn lookup_child(&self, this_ptr: Weak, name: &str) -> Result> { - let inode = match name { - "cap_last_cap" => CapLastCapFileOps::new_inode(this_ptr.clone()), - "pid_max" => PidMaxFileOps::new_inode(this_ptr.clone()), - _ => return_errno!(Errno::ENOENT), - }; - Ok(inode) + fn lookup_child(&self, dir: &ProcDir, name: &str) -> Result> { + let mut cached_children = dir.cached_children().write(); + + if let Some(child) = + lookup_child_from_table(name, &mut cached_children, Self::STATIC_ENTRIES, |f| { + (f)(dir.this_weak().clone()) + }) + { + return Ok(child); + } + + return_errno_with_message!(Errno::ENOENT, "the file does not exist"); } - fn populate_children(&self, this_ptr: Weak) { - let this = { - let this = this_ptr.upgrade().unwrap(); - this.downcast_ref::>().unwrap().this() - }; - let mut cached_children = this.cached_children().write(); - cached_children.put_entry_if_not_found("cap_last_cap", || { - CapLastCapFileOps::new_inode(this_ptr.clone()) + fn populate_children<'a>( + &self, + dir: &'a ProcDir, + ) -> RwMutexUpgradeableGuard<'a, SlotVec<(String, Arc)>> { + let mut cached_children = dir.cached_children().write(); + + populate_children_from_table(&mut cached_children, Self::STATIC_ENTRIES, |f| { + (f)(dir.this_weak().clone()) }); - cached_children - .put_entry_if_not_found("pid_max", || PidMaxFileOps::new_inode(this_ptr.clone())); + + cached_children.downgrade() } } diff --git a/kernel/src/fs/procfs/sys/mod.rs b/kernel/src/fs/procfs/sys/mod.rs index f25ad15db..c5ab00f7e 100644 --- a/kernel/src/fs/procfs/sys/mod.rs +++ b/kernel/src/fs/procfs/sys/mod.rs @@ -1,10 +1,14 @@ // SPDX-License-Identifier: MPL-2.0 +use aster_util::slot_vec::SlotVec; +use ostd::sync::RwMutexUpgradeableGuard; + use self::kernel::KernelDirOps; +use super::template::populate_children_from_table; use crate::{ fs::{ - procfs::template::{DirOps, ProcDir, ProcDirBuilder}, - utils::{mkmod, DirEntryVecExt, Inode}, + procfs::template::{lookup_child_from_table, DirOps, ProcDir, ProcDirBuilder}, + utils::{mkmod, Inode}, }, prelude::*, }; @@ -24,24 +28,37 @@ impl SysDirOps { .build() .unwrap() } + + #[expect(clippy::type_complexity)] + const STATIC_ENTRIES: &'static [(&'static str, fn(Weak) -> Arc)] = + &[("kernel", KernelDirOps::new_inode)]; } impl DirOps for SysDirOps { - fn lookup_child(&self, this_ptr: Weak, name: &str) -> Result> { - let inode = match name { - "kernel" => KernelDirOps::new_inode(this_ptr.clone()), - _ => return_errno!(Errno::ENOENT), - }; - Ok(inode) + fn lookup_child(&self, dir: &ProcDir, name: &str) -> Result> { + let mut cached_children = dir.cached_children().write(); + + if let Some(child) = + lookup_child_from_table(name, &mut cached_children, Self::STATIC_ENTRIES, |f| { + (f)(dir.this_weak().clone()) + }) + { + return Ok(child); + } + + return_errno_with_message!(Errno::ENOENT, "the file does not exist"); } - fn populate_children(&self, this_ptr: Weak) { - let this = { - let this = this_ptr.upgrade().unwrap(); - this.downcast_ref::>().unwrap().this() - }; - let mut cached_children = this.cached_children().write(); - cached_children - .put_entry_if_not_found("kernel", || KernelDirOps::new_inode(this_ptr.clone())) + fn populate_children<'a>( + &self, + dir: &'a ProcDir, + ) -> RwMutexUpgradeableGuard<'a, SlotVec<(String, Arc)>> { + let mut cached_children = dir.cached_children().write(); + + populate_children_from_table(&mut cached_children, Self::STATIC_ENTRIES, |f| { + (f)(dir.this_weak().clone()) + }); + + cached_children.downgrade() } } diff --git a/kernel/src/fs/procfs/template/dir.rs b/kernel/src/fs/procfs/template/dir.rs index 62b5e0aea..0ecff0006 100644 --- a/kernel/src/fs/procfs/template/dir.rs +++ b/kernel/src/fs/procfs/template/dir.rs @@ -1,17 +1,19 @@ // SPDX-License-Identifier: MPL-2.0 -#![expect(unused_variables)] - use core::time::Duration; use aster_util::slot_vec::SlotVec; use inherit_methods_macro::inherit_methods; +use ostd::sync::RwMutexUpgradeableGuard; use super::{Common, ProcFs}; use crate::{ fs::{ path::{is_dot, is_dotdot}, - utils::{DirentVisitor, FileSystem, Inode, InodeMode, InodeType, Metadata, MknodType}, + utils::{ + DirEntryVecExt, DirentVisitor, FileSystem, Inode, InodeMode, InodeType, Metadata, + MknodType, + }, }, prelude::*, process::{Gid, Uid}, @@ -56,6 +58,10 @@ impl ProcDir { self.this.upgrade().unwrap() } + pub fn this_weak(&self) -> &Weak> { + &self.this + } + pub fn parent(&self) -> Option> { self.parent.as_ref().and_then(|p| p.upgrade()) } @@ -96,7 +102,7 @@ impl Inode for ProcDir { Err(Error::new(Errno::EPERM)) } - fn mknod(&self, _name: &str, _mode: InodeMode, type_: MknodType) -> Result> { + fn mknod(&self, _name: &str, _mode: InodeMode, _type_: MknodType) -> Result> { Err(Error::new(Errno::EPERM)) } @@ -120,8 +126,7 @@ impl Inode for ProcDir { } // Read the normal child entries. - self.inner.populate_children(self.this.clone()); - let cached_children = self.cached_children.read(); + let cached_children = self.inner.populate_children(self); let start_offset = *offset; for (idx, (name, child)) in cached_children .idxes_and_items() @@ -154,23 +159,22 @@ impl Inode for ProcDir { } fn lookup(&self, name: &str) -> Result> { - let inode = if is_dot(name) { - self.this() - } else if is_dotdot(name) { - self.parent().unwrap_or(self.this()) - } else { - let mut cached_children = self.cached_children.write(); - if let Some((_, inode)) = cached_children - .iter() - .find(|(child_name, inode)| child_name.as_str() == name) - { - return Ok(inode.clone()); - } - let inode = self.inner.lookup_child(self.this.clone(), name)?; - cached_children.put((String::from(name), inode.clone())); - inode - }; - Ok(inode) + if is_dot(name) { + return Ok(self.this()); + } + if is_dotdot(name) { + return Ok(self.parent().unwrap_or(self.this())); + } + + let cached_children = self.cached_children.read(); + if let Some(inode) = cached_children.find_entry_by_name(name) + && self.inner.validate_child(inode.as_ref()) + { + return Ok(inode.clone()); + } + drop(cached_children); + + self.inner.lookup_child(self, name) } fn rename(&self, _old_name: &str, _target: &Arc, _new_name: &str) -> Result<()> { @@ -182,10 +186,53 @@ impl Inode for ProcDir { } } -pub trait DirOps: Sync + Send { - fn lookup_child(&self, this_ptr: Weak, name: &str) -> Result> { - Err(Error::new(Errno::ENOENT)) +pub trait DirOps: Sync + Send + Sized { + fn lookup_child(&self, dir: &ProcDir, name: &str) -> Result>; + + fn populate_children<'a>( + &self, + dir: &'a ProcDir, + ) -> RwMutexUpgradeableGuard<'a, SlotVec<(String, Arc)>>; + + #[must_use] + fn validate_child(&self, _child: &dyn Inode) -> bool { + true + } +} + +pub fn lookup_child_from_table( + name: &str, + cached_children: &mut SlotVec<(String, Arc)>, + table: &[(&str, Fp)], + constructor_adaptor: F, +) -> Option> +where + Fp: Copy, + F: FnOnce(Fp) -> Arc, +{ + for (child_name, child_constructor) in table.iter() { + if *child_name == name { + return Some( + cached_children + .put_entry_if_not_found(name, || (constructor_adaptor)(*child_constructor)) + .clone(), + ); + } } - fn populate_children(&self, this_ptr: Weak) {} + None +} + +pub fn populate_children_from_table( + cached_children: &mut SlotVec<(String, Arc)>, + table: &[(&str, Fp)], + constructor_adaptor: F, +) where + Fp: Copy, + F: Fn(Fp) -> Arc, +{ + for (child_name, child_constructor) in table.iter() { + cached_children + .put_entry_if_not_found(child_name, || (constructor_adaptor)(*child_constructor)); + } } diff --git a/kernel/src/fs/procfs/template/mod.rs b/kernel/src/fs/procfs/template/mod.rs index 89d2f4d57..3a4297321 100644 --- a/kernel/src/fs/procfs/template/mod.rs +++ b/kernel/src/fs/procfs/template/mod.rs @@ -4,9 +4,9 @@ use core::time::Duration; pub use self::{ builder::{ProcDirBuilder, ProcFileBuilder, ProcSymBuilder}, - dir::{DirOps, ProcDir}, + dir::{lookup_child_from_table, populate_children_from_table, DirOps, ProcDir}, file::FileOps, - sym::SymOps, + sym::{ProcSym, SymOps}, }; use super::{ProcFs, BLOCK_SIZE}; use crate::{ diff --git a/kernel/src/fs/procfs/template/sym.rs b/kernel/src/fs/procfs/template/sym.rs index e8ab9a559..b1e1b7f80 100644 --- a/kernel/src/fs/procfs/template/sym.rs +++ b/kernel/src/fs/procfs/template/sym.rs @@ -31,6 +31,10 @@ impl ProcSym { }; Arc::new(Self { inner: sym, common }) } + + pub fn inner(&self) -> &S { + &self.inner + } } #[inherit_methods(from = "self.common")] diff --git a/kernel/src/fs/utils/access_mode.rs b/kernel/src/fs/utils/access_mode.rs index 0d7bd0e26..4c9df94d8 100644 --- a/kernel/src/fs/utils/access_mode.rs +++ b/kernel/src/fs/utils/access_mode.rs @@ -5,7 +5,7 @@ use aster_rights::Rights; use crate::prelude::*; #[expect(non_camel_case_types)] -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] #[repr(u8)] pub enum AccessMode { /// read only diff --git a/kernel/src/fs/utils/direntry_vec.rs b/kernel/src/fs/utils/direntry_vec.rs index fe7ac1b70..777658741 100644 --- a/kernel/src/fs/utils/direntry_vec.rs +++ b/kernel/src/fs/utils/direntry_vec.rs @@ -6,22 +6,47 @@ use super::Inode; use crate::prelude::*; pub trait DirEntryVecExt { - /// If the entry is not found by `name`, use `f` to get the inode, then put the entry into vec. - fn put_entry_if_not_found(&mut self, name: &str, f: impl Fn() -> Arc); + /// Finds the entry by the `name`. + fn find_entry_by_name(&self, name: &str) -> Option<&Arc>; - /// Remove and returns the entry by name. - /// Returns `None` if the entry has been removed. + /// Puts the entry given by `f` into the vector if it is not found by the `name`. + fn put_entry_if_not_found( + &mut self, + name: &str, + f: impl FnOnce() -> Arc, + ) -> &Arc; + + /// Removes the entry by the `name`. fn remove_entry_by_name(&mut self, name: &str) -> Option<(String, Arc)>; } impl DirEntryVecExt for SlotVec<(String, Arc)> { - fn put_entry_if_not_found(&mut self, name: &str, f: impl Fn() -> Arc) { - if !self.iter().any(|(child_name, _)| child_name == name) { - let inode = f(); - self.put((String::from(name), inode)); + fn find_entry_by_name(&self, name: &str) -> Option<&Arc> { + if let Some((_, inode)) = self.iter().find(|(child_name, _)| child_name == name) { + Some(inode) + } else { + None } } + fn put_entry_if_not_found( + &mut self, + name: &str, + f: impl FnOnce() -> Arc, + ) -> &Arc { + let idx = self + .idxes_and_items() + .find(|(_, (child_name, _))| child_name == name) + .map(|(idx, _)| idx); + let idx = if let Some(idx) = idx { + idx + } else { + let inode = f(); + self.put((String::from(name), inode)) + }; + &self.get(idx).unwrap().1 + } + fn remove_entry_by_name(&mut self, name: &str) -> Option<(String, Arc)> { let idx = self .idxes_and_items()