From d13cdff8953a15d1c1b76cc8f8ad2a8acfe94f96 Mon Sep 17 00:00:00 2001 From: Weikang Guo Date: Thu, 7 May 2026 17:07:16 +0800 Subject: [PATCH 01/21] Refactor posix-types time module Split the monolithic time.rs into a time/ subdirectory with separate files for ABI conversions (abi.rs) and interval timer types (itimer.rs), preparing for reuse by the timer crate extraction. --- posix/types/Cargo.toml | 1 + posix/types/src/{time.rs => time/abi.rs} | 2 +- posix/types/src/time/itimer.rs | 35 ++++++++++++++++++++++++ posix/types/src/time/mod.rs | 11 ++++++++ 4 files changed, 48 insertions(+), 1 deletion(-) rename posix/types/src/{time.rs => time/abi.rs} (99%) create mode 100644 posix/types/src/time/itimer.rs create mode 100644 posix/types/src/time/mod.rs diff --git a/posix/types/Cargo.toml b/posix/types/Cargo.toml index 82ec50eb..28f9b0d1 100644 --- a/posix/types/Cargo.toml +++ b/posix/types/Cargo.toml @@ -15,4 +15,5 @@ kerrno = { workspace = true } khal = { workspace = true } linux-raw-sys = { workspace = true } osvm = { workspace = true } +strum = { workspace = true } unittest = { workspace = true} diff --git a/posix/types/src/time.rs b/posix/types/src/time/abi.rs similarity index 99% rename from posix/types/src/time.rs rename to posix/types/src/time/abi.rs index 1e022563..abcf69ea 100644 --- a/posix/types/src/time.rs +++ b/posix/types/src/time/abi.rs @@ -2,7 +2,7 @@ // Copyright 2025 KylinSoft Co., Ltd. // See LICENSES for license details. -//! Time-related POSIX type conversions. +//! ABI-facing time structure conversions. use kerrno::{KError, KResult}; use khal::time::TimeValue; diff --git a/posix/types/src/time/itimer.rs b/posix/types/src/time/itimer.rs new file mode 100644 index 00000000..0730d35e --- /dev/null +++ b/posix/types/src/time/itimer.rs @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2025 KylinSoft Co., Ltd. +// See LICENSES for license details. + +//! Interval-timer ABI types. + +use strum::FromRepr; + +/// The POSIX/Linux interval timer kind used by `getitimer(2)` / `setitimer(2)`. +#[repr(i32)] +#[allow(non_camel_case_types)] +#[derive(Eq, PartialEq, Debug, Clone, Copy, FromRepr)] +pub enum ITimerType { + /// Wall-clock interval timer. + Real = 0, + /// Per-thread/process user CPU time interval timer. + Virtual = 1, + /// User + kernel CPU time profiling timer. + Prof = 2, +} + +#[cfg(unittest)] +mod tests { + use unittest::def_test; + + use super::ITimerType; + + #[def_test] + fn test_itimer_type_from_repr() { + assert_eq!(ITimerType::from_repr(0), Some(ITimerType::Real)); + assert_eq!(ITimerType::from_repr(1), Some(ITimerType::Virtual)); + assert_eq!(ITimerType::from_repr(2), Some(ITimerType::Prof)); + assert_eq!(ITimerType::from_repr(3), None); + } +} diff --git a/posix/types/src/time/mod.rs b/posix/types/src/time/mod.rs new file mode 100644 index 00000000..96ef7654 --- /dev/null +++ b/posix/types/src/time/mod.rs @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2025 KylinSoft Co., Ltd. +// See LICENSES for license details. + +//! Time-related POSIX/Linux ABI types. + +mod abi; +mod itimer; + +pub use abi::TimeValueLike; +pub use itimer::ITimerType; -- Gitee From 1891c71f9f6a90f8115caa3d0496afffad055872 Mon Sep 17 00:00:00 2001 From: Weikang Guo Date: Thu, 7 May 2026 17:39:20 +0800 Subject: [PATCH 02/21] Extract process management and POSIX syscall implementations into standalone crates Move process/thread runtime, file descriptor management, futex, rlimit, timer, and resource accounting out of kcore/kservices into dedicated process-level crates (kthread, kfd, kfutex, krlimit, kresources, ktimer). Extract signal and futex syscall handlers into posix-signal and posix-sync. Add posix-process for PID/session/process-group syscalls. Update all callers in kcore, kservices, ksyscall, entry, procfs, tee, and existing posix crates to use the new module boundaries. --- Cargo.toml | 12 + core/kcore/Cargo.toml | 8 +- core/kcore/src/lib.rs | 3 - core/kcore/src/task.rs | 655 +----------------- core/kservices/Cargo.toml | 4 +- core/kservices/src/file/mod.rs | 432 +----------- core/kservices/src/file/pidfd.rs | 20 +- core/kservices/src/file/pipe.rs | 6 +- core/kservices/src/file/signalfd.rs | 8 +- core/kservices/src/lib.rs | 2 +- core/kservices/src/mm.rs | 17 +- core/kservices/src/signal.rs | 6 +- core/kservices/src/task.rs | 50 +- core/kservices/src/terminal/job.rs | 4 +- core/kservices/src/terminal/ldisc.rs | 2 +- core/kservices/src/unittest_task.rs | 13 +- core/kservices/src/vfs/dev/dice.rs | 4 +- core/kservices/src/vfs/dev/memtrack.rs | 11 +- core/kservices/src/vfs/dev/tty.rs | 16 +- core/kservices/src/vfs/mod.rs | 14 +- core/ksyscall/Cargo.toml | 6 + core/ksyscall/src/dispatch.rs | 14 +- core/ksyscall/src/fs/event.rs | 3 +- core/ksyscall/src/fs/pidfd.rs | 25 +- core/ksyscall/src/fs/signalfd.rs | 2 +- core/ksyscall/src/fs/timerfd.rs | 3 +- core/ksyscall/src/io_mpx/epoll.rs | 2 +- core/ksyscall/src/io_mpx/mod.rs | 2 +- core/ksyscall/src/io_mpx/poll.rs | 3 +- core/ksyscall/src/io_mpx/select.rs | 6 +- core/ksyscall/src/lib.rs | 1 - core/ksyscall/src/net/cmsg.rs | 3 +- core/ksyscall/src/net/opt.rs | 8 +- core/ksyscall/src/net/socket.rs | 6 +- core/ksyscall/src/resources.rs | 93 +-- core/ksyscall/src/sync/mod.rs | 6 +- core/ksyscall/src/sys.rs | 2 +- core/ksyscall/src/task/clone.rs | 50 +- core/ksyscall/src/task/ctl.rs | 10 +- core/ksyscall/src/task/execve.rs | 28 +- core/ksyscall/src/task/job.rs | 52 +- core/ksyscall/src/task/mod.rs | 5 +- core/ksyscall/src/task/thread.rs | 42 +- core/ksyscall/src/task/wait.rs | 12 +- core/ksyscall/src/time.rs | 14 +- entry/Cargo.toml | 1 + entry/src/entry.rs | 20 +- fs/procfs/Cargo.toml | 3 +- fs/procfs/src/task.rs | 24 +- fs/procfs/src/tee.rs | 18 +- posix/credentials/src/helpers.rs | 4 +- posix/fs/Cargo.toml | 3 +- posix/fs/src/ctl.rs | 8 +- posix/fs/src/fd_ops.rs | 32 +- posix/fs/src/io.rs | 12 +- posix/fs/src/open.rs | 10 +- posix/fs/src/path.rs | 22 +- posix/fs/src/pipe.rs | 3 +- posix/fs/src/stat.rs | 3 +- posix/ipc/Cargo.toml | 3 +- posix/ipc/src/msg.rs | 21 +- posix/ipc/src/shm.rs | 19 +- posix/mm/Cargo.toml | 2 +- posix/mm/src/brk.rs | 22 +- posix/mm/src/mincore.rs | 7 +- posix/mm/src/mmap.rs | 43 +- posix/process/Cargo.toml | 20 + posix/process/src/lib.rs | 171 +++++ posix/sched/Cargo.toml | 2 +- posix/sched/src/lib.rs | 6 +- posix/signal/Cargo.toml | 23 + .../src/signal.rs => posix/signal/src/lib.rs | 190 ++--- posix/sync/Cargo.toml | 20 + .../sync/futex.rs => posix/sync/src/lib.rs | 98 +-- process/kfd/Cargo.toml | 23 + process/kfd/src/lib.rs | 403 +++++++++++ process/kfutex/Cargo.toml | 22 + .../src/futex.rs => process/kfutex/src/lib.rs | 32 +- process/kprocess/Cargo.toml | 4 +- process/kresources/Cargo.toml | 17 + process/kresources/src/lib.rs | 73 ++ process/krlimit/Cargo.toml | 15 + .../krlimit/src/lib.rs | 55 +- process/kthread/Cargo.toml | 37 + process/kthread/src/lib.rs | 64 ++ process/kthread/src/lifecycle_state.rs | 36 + process/kthread/src/posix_state.rs | 65 ++ process/kthread/src/process_state.rs | 275 ++++++++ process/kthread/src/registry.rs | 99 +++ process/kthread/src/runtime_state.rs | 50 ++ process/kthread/src/signal.rs | 152 ++++ .../src/task => process/kthread/src}/stat.rs | 56 +- process/kthread/src/thread/core.rs | 175 +++++ process/kthread/src/thread/current.rs | 35 + process/kthread/src/thread/mod.rs | 11 + process/kthread/src/thread/task_ext.rs | 61 ++ process/kthread/src/timer.rs | 21 + process/ktimer/Cargo.toml | 23 + .../src/time.rs => process/ktimer/src/lib.rs | 154 ++-- tee/tee_kernel/Cargo.toml | 2 +- tee/tee_kernel/src/tee/common/file_ops.rs | 5 +- tee/tee_kernel/src/tee/tee_generic.rs | 7 +- tee/tee_kernel/src/tee/tee_obj.rs | 2 +- tee/tee_kernel/src/tee/tee_session.rs | 75 +- tee/tee_kernel/src/tee/tee_ta_manager.rs | 15 +- util/unittest_support/Cargo.toml | 3 +- util/unittest_support/src/lib.rs | 4 +- 107 files changed, 2609 insertions(+), 1957 deletions(-) create mode 100644 posix/process/Cargo.toml create mode 100644 posix/process/src/lib.rs create mode 100644 posix/signal/Cargo.toml rename core/ksyscall/src/signal.rs => posix/signal/src/lib.rs (56%) create mode 100644 posix/sync/Cargo.toml rename core/ksyscall/src/sync/futex.rs => posix/sync/src/lib.rs (59%) create mode 100644 process/kfd/Cargo.toml create mode 100644 process/kfd/src/lib.rs create mode 100644 process/kfutex/Cargo.toml rename core/kcore/src/futex.rs => process/kfutex/src/lib.rs (94%) create mode 100644 process/kresources/Cargo.toml create mode 100644 process/kresources/src/lib.rs create mode 100644 process/krlimit/Cargo.toml rename core/kcore/src/resources.rs => process/krlimit/src/lib.rs (67%) create mode 100644 process/kthread/Cargo.toml create mode 100644 process/kthread/src/lib.rs create mode 100644 process/kthread/src/lifecycle_state.rs create mode 100644 process/kthread/src/posix_state.rs create mode 100644 process/kthread/src/process_state.rs create mode 100644 process/kthread/src/registry.rs create mode 100644 process/kthread/src/runtime_state.rs create mode 100644 process/kthread/src/signal.rs rename {core/kcore/src/task => process/kthread/src}/stat.rs (81%) create mode 100644 process/kthread/src/thread/core.rs create mode 100644 process/kthread/src/thread/current.rs create mode 100644 process/kthread/src/thread/mod.rs create mode 100644 process/kthread/src/thread/task_ext.rs create mode 100644 process/kthread/src/timer.rs create mode 100644 process/ktimer/Cargo.toml rename core/kcore/src/time.rs => process/ktimer/src/lib.rs (76%) diff --git a/Cargo.toml b/Cargo.toml index 1d6f4ec1..d338a37e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -64,6 +64,12 @@ ktask = { path = "task/ktask" } ksched = { path = "task/ksched" } # Internal: process management +kfutex = { path = "process/kfutex" } +kfd = { path = "process/kfd" } +krlimit = { path = "process/krlimit" } +kresources = { path = "process/kresources" } +kthread = { path = "process/kthread" } +ktimer = { path = "process/ktimer" } kprocess = { path = "process/kprocess" } osvm = { path = "process/osvm" } @@ -72,7 +78,10 @@ posix-credentials = { path = "posix/credentials" } posix-ipc = { path = "posix/ipc" } posix-fs = { path = "posix/fs" } posix-mm = { path = "posix/mm" } +posix-process = { path = "posix/process" } posix-sched = { path = "posix/sched" } +posix-signal = { path = "posix/signal" } +posix-sync = { path = "posix/sync" } posix-types = { path = "posix/types" } # Internal: memory and filesystem @@ -190,3 +199,6 @@ fs9p = { path = "fs/fs9p", default-features = false } ktracepoint = "0.5" static-keys = "0.8" buddy-slab-allocator = "0.4" +downcast-rs = { version = "2.0", default-features = false, features = ["sync"] } +flatten_objects = "0.2.4" +weak-map = "0.1.1" diff --git a/core/kcore/Cargo.toml b/core/kcore/Cargo.toml index a6949f58..8d9d5d2f 100644 --- a/core/kcore/Cargo.toml +++ b/core/kcore/Cargo.toml @@ -24,24 +24,20 @@ bitflags.workspace = true cfg-if.workspace = true event-listener.workspace = true extern-trait.workspace = true -hashbrown = { workspace = true } inherit-methods-macro = "0.1.0" kernel-elf-parser = { workspace = true } kspin.workspace = true lazy_static = { workspace = true } linkme.workspace = true -linux-raw-sys.workspace = true memaddr.workspace = true ouroboros = { version = "0.18.5", default-features = false } percpu = { workspace = true } scope-local.workspace = true slab.workspace = true -kprocess.workspace = true -kcred.workspace = true ksignal.workspace = true +kthread.workspace = true osvm.workspace = true strum = { workspace = true } -weak-map = "0.1.1" xmas-elf = "0.9" unittest.workspace = true kbuild_config = { workspace = true } @@ -51,5 +47,5 @@ tee_task_iface = { workspace = true, optional = true } memspace = { workspace = true, features = ["copy"] } [features] -tee = ["dep:tee_task_iface"] +tee = ["dep:tee_task_iface", "kthread/tee"] tee_ta_sign = [] diff --git a/core/kcore/src/lib.rs b/core/kcore/src/lib.rs index ae20c9bd..b598475a 100644 --- a/core/kcore/src/lib.rs +++ b/core/kcore/src/lib.rs @@ -16,10 +16,7 @@ extern crate alloc; extern crate klogger; pub mod config; -pub mod futex; mod lrucache; pub mod mm; -pub mod resources; pub mod task; -pub mod time; pub mod vfs; diff --git a/core/kcore/src/task.rs b/core/kcore/src/task.rs index 16e5a330..704ca4af 100644 --- a/core/kcore/src/task.rs +++ b/core/kcore/src/task.rs @@ -2,657 +2,6 @@ // Copyright 2025 KylinSoft Co., Ltd. // See LICENSES for license details. -//! User task management. +//! User task management re-exported from [`kthread`]. -mod stat; - -use alloc::{ - boxed::Box, - string::String, - sync::{Arc, Weak}, - vec::Vec, -}; -use core::{ - cell::RefCell, - ops::Deref, - sync::atomic::{AtomicBool, AtomicI32, AtomicU32, AtomicUsize, Ordering}, -}; - -use extern_trait::extern_trait; -use hashbrown::HashMap; -use kcred::Credentials; -use kerrno::{KError, KResult}; -use kpoll::PollSet; -use kprocess::{Pid, Process, ProcessGroup, Session}; -use ksignal::{ - SignalInfo, Signo, - api::{ProcessSignalManager, SignalActions, ThreadSignalManager}, -}; -use ksync::{Mutex, RwLock, spin::SpinNoIrq}; -use ktask::{KtaskRef, TaskExt, TaskInner, WeakKtaskRef, current}; -use lazy_static::lazy_static; -use memspace::AddrSpace; -use scope_local::{ActiveScope, Scope}; -#[cfg(feature = "tee")] -pub use tee_task_iface::{TeeSessionCtxTrait, TeeTaCtx}; -use weak_map::WeakMap; - -pub use self::stat::TaskStat; -use crate::{ - futex::{FutexKey, FutexTable}, - resources::Rlimits, - time::{TimeManager, TimerState}, -}; - -/// A wrapper type that assumes the inner type is `Sync`. -#[repr(transparent)] -pub struct AssumeSync(pub T); - -unsafe impl Sync for AssumeSync {} - -impl Deref for AssumeSync { - type Target = T; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -/// The inner data of a thread. -pub struct Thread { - /// The process data shared by all threads in the process. - pub proc_data: Arc, - - /// The clear thread tid field - /// - /// See - /// - /// When the thread exits, the kernel clears the word at this address if it - /// is not NULL. - clear_child_tid: AtomicUsize, - - /// The head of the robust list - robust_list_head: AtomicUsize, - - /// The thread-level signal manager - pub signal: Arc, - - /// Time manager - /// - /// This is assumed to be `Sync` because it's only borrowed mutably during - /// context switches, which is exclusive to the current thread. - pub time: AssumeSync>, - - /// The OOM score adjustment value. - oom_score_adj: AtomicI32, - - /// Ready to exit - exit: AtomicBool, - - /// Indicates whether the thread is currently accessing user memory. - accessing_user_memory: AtomicBool, - - /// Tee session context - #[cfg(feature = "tee")] - pub tee_session_ctx: Mutex>>, -} - -impl Thread { - /// Create a new [`Thread`]. - pub fn new(tid: u32, proc_data: Arc) -> Box { - Box::new(Thread { - signal: ThreadSignalManager::new(tid, proc_data.signal.clone()), - proc_data, - clear_child_tid: AtomicUsize::new(0), - robust_list_head: AtomicUsize::new(0), - time: AssumeSync(RefCell::new(TimeManager::new())), - oom_score_adj: AtomicI32::new(200), - exit: AtomicBool::new(false), - accessing_user_memory: AtomicBool::new(false), - #[cfg(feature = "tee")] - tee_session_ctx: Mutex::new(None), - }) - } - - /// Get the clear child tid field. - pub fn clear_child_tid(&self) -> usize { - self.clear_child_tid.load(Ordering::Relaxed) - } - - /// Set the clear child tid field. - pub fn set_clear_child_tid(&self, clear_child_tid: usize) { - self.clear_child_tid - .store(clear_child_tid, Ordering::Relaxed); - } - - /// Get the robust list head. - pub fn robust_list_head(&self) -> usize { - self.robust_list_head.load(Ordering::SeqCst) - } - - /// Set the robust list head. - pub fn set_robust_list_head(&self, robust_list_head: usize) { - self.robust_list_head - .store(robust_list_head, Ordering::SeqCst); - } - - /// Get the oom score adjustment value. - pub fn oom_score_adj(&self) -> i32 { - self.oom_score_adj.load(Ordering::SeqCst) - } - - /// Set the oom score adjustment value. - pub fn set_oom_score_adj(&self, value: i32) { - self.oom_score_adj.store(value, Ordering::SeqCst); - } - - /// Check if the thread is ready to exit. - pub fn pending_exit(&self) -> bool { - self.exit.load(Ordering::Acquire) - } - - /// Set the thread to exit. - pub fn set_exit(&self) { - self.exit.store(true, Ordering::Release); - } - - /// Check if the thread is accessing user memory. - pub fn is_accessing_user_memory(&self) -> bool { - self.accessing_user_memory.load(Ordering::Acquire) - } - - /// Set the accessing user memory flag. - pub fn set_accessing_user_memory(&self, accessing: bool) { - self.accessing_user_memory - .store(accessing, Ordering::Release); - } - - /// Set the tee session context. - #[cfg(feature = "tee")] - pub fn set_tee_session_ctx(&self, ctx: Box) { - let mut guard = self.tee_session_ctx.lock(); - if guard.is_none() { - *guard = Some(ctx); - } - } -} - -#[extern_trait] -unsafe impl TaskExt for Box { - fn on_enter(&self) { - let scope = self.proc_data.scope.read(); - unsafe { ActiveScope::set(&scope) }; - core::mem::forget(scope); - } - - fn on_leave(&self) { - ActiveScope::set_global(); - unsafe { self.proc_data.scope.force_unlock_read() }; - } -} - -/// Helper trait to access the thread from a task. -pub trait AsThread { - /// Try to get the thread from the task. - fn try_as_thread(&self) -> Option<&Thread>; - - /// Get the thread from the task, panicking if it is a kernel task. - fn as_thread(&self) -> &Thread { - self.try_as_thread().expect("kernel task") - } -} - -impl AsThread for TaskInner { - fn try_as_thread(&self) -> Option<&Thread> { - self.task_ext() - .map(|ext| unsafe { ext.downcast_ref::>() }.as_ref()) - } -} - -/// [`Process`]-shared data. -pub struct ProcessData { - /// The process. - pub proc: Arc, - /// The executable path - pub exe_path: RwLock, - /// The command line arguments - pub cmdline: RwLock>>, - /// The virtual memory address space. - // TODO: scopify - pub aspace: Arc>, - /// The resource scope - pub scope: RwLock, - /// The user heap top - heap_top: AtomicUsize, - - /// The resource limits - pub rlim: RwLock, - - /// The child exit wait event - pub child_exit_event: Arc, - /// Self exit event - pub exit_event: Arc, - /// The exit signal of the thread - pub exit_signal: Option, - - /// The process signal manager - pub signal: Arc, - - /// The futex table. - futex_table: Arc, - - /// The default mask for file permissions. - umask: AtomicU32, - - /// POSIX credentials shared by all threads in this process. - pub credentials: RwLock, - - /// Tee TA context - #[cfg(feature = "tee")] - pub tee_ta_ctx: RwLock, -} - -impl ProcessData { - /// Create a new [`ProcessData`]. - pub fn new( - proc: Arc, - exe_path: String, - cmdline: Arc>, - aspace: Arc>, - signal_actions: Arc>, - exit_signal: Option, - credentials: Credentials, - ) -> Arc { - Arc::new(Self { - proc, - #[cfg(feature = "tee")] - tee_ta_ctx: RwLock::new(TeeTaCtx::new(&exe_path)), - exe_path: RwLock::new(exe_path), - cmdline: RwLock::new(cmdline), - aspace, - scope: RwLock::new(Scope::new()), - heap_top: AtomicUsize::new(crate::config::USER_HEAP_BASE), - - rlim: RwLock::default(), - - child_exit_event: Arc::default(), - exit_event: Arc::default(), - exit_signal, - - signal: Arc::new(ProcessSignalManager::new( - signal_actions, - crate::config::SIGNAL_TRAMPOLINE, - )), - - futex_table: Arc::new(FutexTable::new()), - - umask: AtomicU32::new(0o022), - - credentials: RwLock::new(credentials), - }) - } - - /// Get the top address of the user heap. - pub fn get_heap_top(&self) -> usize { - self.heap_top.load(Ordering::Acquire) - } - - /// Set the top address of the user heap. - pub fn set_heap_top(&self, top: usize) { - self.heap_top.store(top, Ordering::Release) - } - - /// Linux manual: A "clone" child is one which delivers no signal, or a - /// signal other than SIGCHLD to its parent upon termination. - pub fn is_clone_child(&self) -> bool { - self.exit_signal != Some(Signo::SIGCHLD) - } - - /// Returns the futex table for the given key. - pub fn futex_table_for(&self, key: &FutexKey) -> Arc { - match key { - FutexKey::Private { .. } => self.futex_table.clone(), - FutexKey::Shared { region, .. } => { - let ptr = match region { - Ok(pages) => Weak::as_ptr(pages) as usize, - Err(key) => Weak::as_ptr(key) as usize, - }; - SHARED_FUTEX_TABLES.lock().get_or_insert(ptr) - } - } - } - - /// Get the umask. - pub fn umask(&self) -> u32 { - self.umask.load(Ordering::SeqCst) - } - - /// Set the umask. - pub fn set_umask(&self, umask: u32) { - self.umask.store(umask, Ordering::SeqCst); - } - - /// Set the umask and return the old value. - pub fn replace_umask(&self, umask: u32) -> u32 { - self.umask.swap(umask, Ordering::SeqCst) - } -} - -struct FutexTables { - map: HashMap>, - operations: usize, -} -impl FutexTables { - fn new() -> Self { - Self { - map: HashMap::new(), - operations: 0, - } - } - - fn get_or_insert(&mut self, key: usize) -> Arc { - self.operations += 1; - if self.operations == 100 { - self.operations = 0; - self.map - .retain(|_, table| Arc::strong_count(table) > 1 || !table.is_empty()); - } - self.map - .entry(key) - .or_insert_with(|| Arc::new(FutexTable::new())) - .clone() - } -} - -lazy_static! { - static ref SHARED_FUTEX_TABLES: Mutex = Mutex::new(FutexTables::new()); -} - -static TASK_TABLE: RwLock> = RwLock::new(WeakMap::new()); - -static PROCESS_TABLE: RwLock>> = RwLock::new(WeakMap::new()); - -static PROCESS_GROUP_TABLE: RwLock>> = RwLock::new(WeakMap::new()); - -static SESSION_TABLE: RwLock>> = RwLock::new(WeakMap::new()); - -/// Cleanup expired entries in the task tables. -/// -/// This function is intended to be used during memory leak analysis to remove -/// possible noise caused by expired entries in the [`WeakMap`]. -pub fn cleanup_task_tables() { - TASK_TABLE.write().cleanup(); - PROCESS_TABLE.write().cleanup(); - PROCESS_GROUP_TABLE.write().cleanup(); - SESSION_TABLE.write().cleanup(); -} - -/// Add the task, the thread and possibly its process, process group and session -/// to the corresponding tables. -pub fn add_task_to_table(task: &KtaskRef) { - let tid = task.id().as_u64() as Pid; - - let mut task_table = TASK_TABLE.write(); - task_table.insert(tid, task); - - let proc_data = &task.as_thread().proc_data; - let proc = &proc_data.proc; - let pid = proc.pid(); - let mut proc_table = PROCESS_TABLE.write(); - if proc_table.contains_key(&pid) { - return; - } - proc_table.insert(pid, proc_data); - - let pg = proc.group(); - let mut pg_table = PROCESS_GROUP_TABLE.write(); - if pg_table.contains_key(&pg.pgid()) { - return; - } - pg_table.insert(pg.pgid(), &pg); - - let session = pg.session(); - let mut session_table = SESSION_TABLE.write(); - if session_table.contains_key(&session.sid()) { - return; - } - session_table.insert(session.sid(), &session); -} - -/// Lists all tasks. -pub fn tasks() -> Vec { - TASK_TABLE.read().values().collect() -} - -/// Finds the task with the given TID. -pub fn get_task(tid: Pid) -> KResult { - if tid == 0 { - return Ok(current().clone()); - } - TASK_TABLE.read().get(&tid).ok_or(KError::NoSuchProcess) -} - -/// Lists all processes. -pub fn processes() -> Vec> { - PROCESS_TABLE.read().values().collect() -} - -/// Finds the process with the given PID. -pub fn get_process_data(pid: Pid) -> KResult> { - if pid == 0 { - return Ok(current().as_thread().proc_data.clone()); - } - PROCESS_TABLE.read().get(&pid).ok_or(KError::NoSuchProcess) -} - -/// Finds the process group with the given PGID. -pub fn get_process_group(pgid: Pid) -> KResult> { - PROCESS_GROUP_TABLE - .read() - .get(&pgid) - .ok_or(KError::NoSuchProcess) -} - -/// Finds the session with the given SID. -pub fn get_session(sid: Pid) -> KResult> { - SESSION_TABLE.read().get(&sid).ok_or(KError::NoSuchProcess) -} - -/// Poll the timer -pub fn poll_timer(task: &TaskInner) { - let Some(thr) = task.try_as_thread() else { - return; - }; - let Ok(mut time) = thr.time.try_borrow_mut() else { - // reentrant borrow, likely IRQ - return; - }; - time.poll(|signo| { - send_signal_thread_inner(task, thr, SignalInfo::new_kernel(signo)); - }); -} - -/// Sets the timer state. -pub fn set_timer_state(task: &TaskInner, state: TimerState) { - let Some(thr) = task.try_as_thread() else { - return; - }; - let Ok(mut time) = thr.time.try_borrow_mut() else { - // reentrant borrow, likely IRQ - return; - }; - time.poll(|signo| { - send_signal_thread_inner(task, thr, SignalInfo::new_kernel(signo)); - }); - time.set_state(state); -} - -fn send_signal_thread_inner(task: &TaskInner, thr: &Thread, sig: SignalInfo) { - if thr.signal.send_signal(sig) { - task.interrupt(); - } -} - -/// Sends a signal to a thread. -pub fn send_signal_to_thread(tgid: Option, tid: Pid, sig: Option) -> KResult<()> { - let task = get_task(tid)?; - let thread = task.try_as_thread().ok_or(KError::OperationNotPermitted)?; - if tgid.is_some_and(|tgid| thread.proc_data.proc.pid() != tgid) { - return Err(KError::NoSuchProcess); - } - - if let Some(sig) = sig { - debug!("Send signal {:?} to thread {}", sig.signo(), tid); - send_signal_thread_inner(&task, thread, sig); - } - - Ok(()) -} - -/// Sends a signal to a process. -pub fn send_signal_to_process(pid: Pid, sig: Option) -> KResult<()> { - let proc_data = get_process_data(pid)?; - - if let Some(sig) = sig { - let signo = sig.signo(); - debug!("Send signal {signo:?} to process {pid}"); - if let Some(tid) = proc_data.signal.send_signal(sig) - && let Ok(task) = get_task(tid) - { - task.interrupt(); - } - } - - Ok(()) -} - -/// Sends a signal to a process group. -pub fn send_signal_to_process_group(pgid: Pid, sig: Option) -> KResult<()> { - let pg = get_process_group(pgid)?; - - if let Some(sig) = sig { - info!("Send signal {:?} to process group {}", sig.signo(), pgid); - for proc in pg.processes() { - send_signal_to_process(proc.pid(), Some(sig.clone()))?; - } - } - - Ok(()) -} - -/// Unit tests. -#[cfg(unittest)] -pub mod tests_task { - use alloc::sync::Arc; - - use kerrno::KError; - use ksignal::SignalInfo; - use unittest::def_test; - - use super::{ - AssumeSync, FutexTables, TaskStat, cleanup_task_tables, get_process_data, - get_process_group, get_session, get_task, processes, send_signal_to_process, - send_signal_to_process_group, send_signal_to_thread, tasks, - }; - - #[def_test] - fn test_assume_sync_deref() { - let value = AssumeSync(42_u32); - assert_eq!(*value, 42); - } - - #[def_test] - fn test_taskstat_display_default() { - let stat = TaskStat::default(); - let text = alloc::format!("{stat}"); - let text = text.trim_end(); - assert!(text.starts_with("0 (")); - assert!(text.ends_with(" 0")); - } - - #[def_test] - fn test_taskstat_display_custom() { - let stat = TaskStat { - pid: 7, - comm: "init".into(), - state: 'R', - ..Default::default() - }; - let text = alloc::format!("{stat}"); - assert!(text.starts_with("7 (init) R ")); - } - - #[def_test] - fn test_futextables_get_or_insert_reuses_existing_table() { - let mut tables = FutexTables::new(); - let first = tables.get_or_insert(0x1234); - let second = tables.get_or_insert(0x1234); - - assert!(Arc::ptr_eq(&first, &second)); - assert_eq!(tables.map.len(), 1); - assert_eq!(tables.operations, 2); - } - - #[def_test] - fn test_futextables_cleanup_drops_stale_entries_on_threshold() { - let mut tables = FutexTables::new(); - let stale = tables.get_or_insert(1); - drop(stale); - - tables.operations = 99; - let fresh = tables.get_or_insert(2); - - assert_eq!(tables.operations, 0); - assert!(!tables.map.contains_key(&1)); - assert!(tables.map.contains_key(&2)); - assert_eq!(Arc::strong_count(&fresh), 2); - } - - #[def_test] - fn test_cleanup_task_tables_on_empty_tables() { - cleanup_task_tables(); - } - - #[def_test] - fn test_task_tables_empty_queries_return_empty_or_not_found() { - cleanup_task_tables(); - - let _ = tasks(); - let _ = processes(); - assert!(matches!(get_task(12345), Err(KError::NoSuchProcess))); - assert!(matches!( - get_process_data(12345), - Err(KError::NoSuchProcess) - )); - assert!(matches!( - get_process_group(12345), - Err(KError::NoSuchProcess) - )); - assert!(matches!(get_session(12345), Err(KError::NoSuchProcess))); - } - - #[def_test] - fn test_send_signal_helpers_propagate_missing_target_errors() { - cleanup_task_tables(); - - assert!(matches!( - send_signal_to_thread( - None, - 22222, - Some(SignalInfo::new_kernel(ksignal::Signo::SIGTERM)) - ), - Err(KError::NoSuchProcess) - )); - assert!(matches!( - send_signal_to_process(22222, Some(SignalInfo::new_kernel(ksignal::Signo::SIGTERM))), - Err(KError::NoSuchProcess) - )); - assert!(matches!( - send_signal_to_process_group( - 22222, - Some(SignalInfo::new_kernel(ksignal::Signo::SIGTERM)) - ), - Err(KError::NoSuchProcess) - )); - } -} +pub use kthread::*; diff --git a/core/kservices/Cargo.toml b/core/kservices/Cargo.toml index b23037e7..082fc5f4 100644 --- a/core/kservices/Cargo.toml +++ b/core/kservices/Cargo.toml @@ -22,6 +22,7 @@ dice = [ ] tee = [ "dep:tee_kernel", + "kthread/tee", "procfs/tee", ] @@ -33,6 +34,7 @@ unittest_support.workspace = true mbedtls = { workspace = true, optional = true } dice_driver = { workspace = true, optional = true } fbdevice.workspace = true +kfd.workspace = true kdriver.workspace = true ktypes.workspace = true kerrno.workspace = true @@ -53,7 +55,6 @@ bitmaps = { version = "3.2.1", default-features = false } bytemuck.workspace = true lazyinit.workspace = true chrono = { version = "0.4.41", default-features = false } -downcast-rs = { version = "2.0", default-features = false, features = ["sync"] } flatten_objects = "0.2.4" gimli = { version = "*", default-features = false, optional = true } hashbrown = { workspace = true } @@ -71,6 +72,7 @@ ringbuf = { version = "0.4.8", default-features = false, features = ["alloc"] } scope-local.workspace = true slab.workspace = true kprocess.workspace = true +kthread.workspace = true ksignal.workspace = true osvm.workspace = true zerocopy = { version = "0.8.26", features = ["derive"] } diff --git a/core/kservices/src/file/mod.rs b/core/kservices/src/file/mod.rs index 58696a73..7a0039c2 100644 --- a/core/kservices/src/file/mod.rs +++ b/core/kservices/src/file/mod.rs @@ -2,7 +2,7 @@ // Copyright 2025 KylinSoft Co., Ltd. // See LICENSES for license details. -//! File descriptor abstractions and file-like traits. +//! File descriptor abstractions and concrete file-like implementations. pub mod epoll; pub mod event; @@ -13,23 +13,16 @@ mod pipe; pub mod signalfd; pub mod timerfd; -use alloc::{borrow::Cow, sync::Arc}; -use core::{ffi::c_int, time::Duration}; +use alloc::sync::Arc; -use downcast_rs::{DowncastSync, impl_downcast}; -use flatten_objects::FlattenObjects; -use fs_ng_vfs::DeviceId; -use kcore::{resources::FILE_LIMIT, task::AsThread}; use kerrno::{KError, KResult}; -use kfs::{FS_CONTEXT, OpenOptions}; -use kio::prelude::*; -use kpoll::Pollable; -use ksync::RwLock; -use ktask::current; -use linux_raw_sys::general::{ - O_RDONLY, O_WRONLY, RLIMIT_NOFILE, S_IFMT, S_IFREG, STATX_ATTR_WRITE_ATOMIC, - STATX_WRITE_ATOMIC, stat, statx, statx_timestamp, +pub use kfd::{ + FileLike, IoDst, IoSrc, Kstat, ReadBuf, WriteBuf, add_file_like, close_all_fds, + close_file_like, current_fd_table, get_file_like, new_fd_table, }; +use kfd::{FdTable, FileDescriptor}; +use kfs::{FS_CONTEXT, OpenOptions}; +use linux_raw_sys::general::{O_RDONLY, O_WRONLY}; pub use self::{ fs::{Directory, File, ResolveAtResult, metadata_to_kstat, resolve_at, with_fs}, @@ -38,414 +31,7 @@ pub use self::{ pipe::Pipe, }; -/// Kernel stat structure containing file metadata. -/// -/// This structure mirrors the POSIX `stat` structure and contains information about a file, -/// including device IDs, inode number, permissions, sizes, and timestamps. -#[derive(Debug, Clone, Copy)] -pub struct Kstat { - /// Device ID of the filesystem containing the file. - pub dev: u64, - /// Inode number. - pub ino: u64, - /// Number of hard links. - pub nlink: u32, - /// File mode and permissions. - pub mode: u32, - /// Owner user ID. - pub uid: u32, - /// Owner group ID. - pub gid: u32, - /// File size in bytes. - pub size: u64, - /// Preferred I/O block size. - pub blksize: u32, - /// Number of allocated blocks. - pub blocks: u64, - /// Device ID for special files. - pub rdev: DeviceId, - /// Last access time. - pub atime: Duration, - /// Last modification time. - pub mtime: Duration, - /// Last status change time. - pub ctime: Duration, -} - -impl Default for Kstat { - fn default() -> Self { - Self { - dev: 0, - ino: 1, - nlink: 1, - mode: 0, - uid: 1, - gid: 1, - size: 0, - blksize: 4096, - blocks: 0, - rdev: DeviceId::default(), - atime: Duration::default(), - mtime: Duration::default(), - ctime: Duration::default(), - } - } -} - -impl From for stat { - fn from(value: Kstat) -> Self { - // SAFETY: valid for stat - let mut stat: stat = unsafe { core::mem::zeroed() }; - stat.st_dev = value.dev as _; - stat.st_ino = value.ino as _; - stat.st_nlink = value.nlink as _; - stat.st_mode = value.mode as _; - stat.st_uid = value.uid as _; - stat.st_gid = value.gid as _; - stat.st_size = value.size as _; - stat.st_blksize = value.blksize as _; - stat.st_blocks = value.blocks as _; - stat.st_rdev = value.rdev.0 as _; - - stat.st_atime = value.atime.as_secs() as _; - stat.st_atime_nsec = value.atime.subsec_nanos() as _; - stat.st_mtime = value.mtime.as_secs() as _; - stat.st_mtime_nsec = value.mtime.subsec_nanos() as _; - stat.st_ctime = value.ctime.as_secs() as _; - stat.st_ctime_nsec = value.ctime.subsec_nanos() as _; - - stat - } -} - -impl From for statx { - fn from(value: Kstat) -> Self { - const ATOMIC_WRITE_UNIT: u32 = 4096; - - // SAFETY: valid for statx - let mut statx: statx = unsafe { core::mem::zeroed() }; - statx.stx_blksize = value.blksize as _; - statx.stx_attributes = 0; - statx.stx_attributes_mask = STATX_ATTR_WRITE_ATOMIC as _; - statx.stx_nlink = value.nlink as _; - statx.stx_uid = value.uid as _; - statx.stx_gid = value.gid as _; - statx.stx_mode = value.mode as _; - statx.stx_ino = value.ino as _; - statx.stx_size = value.size as _; - statx.stx_blocks = value.blocks as _; - statx.stx_rdev_major = value.rdev.major(); - statx.stx_rdev_minor = value.rdev.minor(); - - fn time_to_statx(time: &Duration) -> statx_timestamp { - statx_timestamp { - tv_sec: time.as_secs() as _, - tv_nsec: time.subsec_nanos() as _, - __reserved: 0, - } - } - statx.stx_atime = time_to_statx(&value.atime); - statx.stx_ctime = time_to_statx(&value.ctime); - statx.stx_mtime = time_to_statx(&value.mtime); - - statx.stx_dev_major = (value.dev >> 32) as _; - statx.stx_dev_minor = value.dev as _; - - if value.mode & S_IFMT == S_IFREG { - statx.stx_attributes |= STATX_ATTR_WRITE_ATOMIC as u64; - statx.stx_atomic_write_unit_min = ATOMIC_WRITE_UNIT; - statx.stx_atomic_write_unit_max = ATOMIC_WRITE_UNIT; - statx.stx_atomic_write_unit_max_opt = ATOMIC_WRITE_UNIT; - statx.stx_atomic_write_segments_max = 1; - statx.stx_mask |= STATX_WRITE_ATOMIC; - } - - statx - } -} - -/// Trait for types that can be used as write destinations in I/O operations. -pub trait WriteBuf: Write + IoBufMut {} -impl WriteBuf for T {} -/// I/O destination buffer type for write operations. -pub type IoDst<'a> = dyn WriteBuf + 'a; - -/// Trait for types that can be used as read sources in I/O operations. -pub trait ReadBuf: Read + IoBuf {} -impl ReadBuf for T {} -/// I/O source buffer type for read operations. -pub type IoSrc<'a> = dyn ReadBuf + 'a; - -/// Trait for file-like objects that support standard file operations. -/// -/// This trait abstracts various file-like objects (regular files, directories, sockets, pipes, etc.) -/// and provides a unified interface for I/O, metadata retrieval, and control operations. -#[allow(dead_code)] -pub trait FileLike: Pollable + DowncastSync { - /// Reads data from the file into the provided buffer. - fn read(&self, _dst: &mut IoDst) -> KResult { - Err(KError::InvalidInput) - } - - /// Writes data from the provided buffer to the file. - fn write(&self, _src: &mut IoSrc) -> KResult { - Err(KError::InvalidInput) - } - - /// Gets file metadata and statistics. - fn stat(&self) -> KResult { - Ok(Kstat::default()) - } - - /// Returns the absolute path of this file. - fn path(&self) -> Cow<'_, str>; - - /// Performs I/O control operations. - fn ioctl(&self, _cmd: u32, _arg: usize) -> KResult { - Err(KError::NotATty) - } - - /// Returns the open flags for this file (e.g., O_RDONLY, O_WRONLY, O_RDWR). - fn open_flags(&self) -> u32 { - 0 - } - - /// Returns whether this file is in non-blocking mode. - fn nonblocking(&self) -> bool { - false - } - - /// Sets or clears the non-blocking flag. - fn set_nonblocking(&self, _nonblocking: bool) -> KResult { - Ok(()) - } - - /// Converts a file descriptor to a file-like reference. - fn from_fd(fd: c_int) -> KResult> - where - Self: Sized + 'static, - { - get_file_like(fd)? - .downcast_arc() - .map_err(|_| KError::InvalidInput) - } - - /// Adds this file-like object to the current process's file descriptor table. - fn add_to_fd_table(self, cloexec: bool) -> KResult - where - Self: Sized + 'static, - { - add_file_like(Arc::new(self), cloexec) - } -} -impl_downcast!(sync FileLike); - -/// A file descriptor entry in the file descriptor table. -/// -/// Contains a reference to the file-like object and flags like close-on-exec. -#[derive(Clone)] -pub struct FileDescriptor { - pub inner: Arc, - /// Close-on-exec flag (true if file should be closed on exec) - pub cloexec: bool, -} - -fn new_fd_table() -> Arc>> { - let mut table = Arc::>>::new_uninit(); - Arc::get_mut(&mut table) - .expect("newly allocated Arc must be unique") - .write(RwLock::new(FlattenObjects::new())); - unsafe { table.assume_init() } -} - -scope_local::scope_local! { - /// The current file descriptor table. - pub static FD_TABLE: Arc>> = new_fd_table(); -} - -/// Retrieves a file-like object from the file descriptor table. -/// -/// # Arguments -/// - `fd`: The file descriptor to look up -/// -/// # Returns -/// A reference to the file-like object, or `BadFileDescriptor` error if not found. -pub fn get_file_like(fd: c_int) -> KResult> { - FD_TABLE - .read() - .get(fd as usize) - .map(|fd| fd.inner.clone()) - .ok_or(KError::BadFileDescriptor) -} - -/// Adds a file-like object to the current process's file descriptor table. -/// -/// # Arguments -/// - `f`: The file-like object to add -/// - `cloexec`: Whether to set the close-on-exec flag -/// -/// # Returns -/// The new file descriptor number, or an error if the table is full. -pub fn add_file_like(f: Arc, cloexec: bool) -> KResult { - let max_nofile = current().as_thread().proc_data.rlim.read()[RLIMIT_NOFILE].current; - let mut table = FD_TABLE.write(); - if table.count() as u64 >= max_nofile { - return Err(KError::TooManyOpenFiles); - } - let fd = FileDescriptor { inner: f, cloexec }; - Ok(table.add(fd).map_err(|_| KError::TooManyOpenFiles)? as c_int) -} - -/// Closes a file descriptor and removes it from the file descriptor table. -/// -/// # Arguments -/// - `fd`: The file descriptor to close -pub fn close_file_like(fd: c_int) -> KResult { - let f = FD_TABLE - .write() - .remove(fd as usize) - .ok_or(KError::BadFileDescriptor)?; - debug!("close_file_like <= count: {}", Arc::strong_count(&f.inner)); - Ok(()) -} - -#[cfg(unittest)] -mod kstat_tests { - use unittest::def_test; - - use super::*; - - #[def_test] - fn test_kstat_default() { - let kstat = Kstat::default(); - assert_eq!(kstat.dev, 0); - assert_eq!(kstat.ino, 1); - assert_eq!(kstat.nlink, 1); - assert_eq!(kstat.mode, 0); - assert_eq!(kstat.uid, 1); - assert_eq!(kstat.gid, 1); - assert_eq!(kstat.size, 0); - assert_eq!(kstat.blksize, 4096); - assert_eq!(kstat.blocks, 0); - assert_eq!(kstat.atime, Duration::default()); - assert_eq!(kstat.mtime, Duration::default()); - assert_eq!(kstat.ctime, Duration::default()); - } - - #[def_test] - fn test_kstat_to_stat() { - let kstat = Kstat { - dev: 42, - ino: 100, - nlink: 3, - mode: 0o755, - uid: 1000, - gid: 1000, - size: 4096, - blksize: 512, - blocks: 8, - rdev: DeviceId::default(), - atime: Duration::new(1000, 500_000_000), - mtime: Duration::new(2000, 0), - ctime: Duration::new(3000, 123_456_789), - }; - - let s: stat = kstat.into(); - assert_eq!(s.st_dev, 42); - assert_eq!(s.st_ino, 100); - assert_eq!(s.st_nlink, 3); - assert_eq!(s.st_mode, 0o755); - assert_eq!(s.st_uid, 1000); - assert_eq!(s.st_gid, 1000); - assert_eq!(s.st_size, 4096); - assert_eq!(s.st_blksize, 512); - assert_eq!(s.st_blocks, 8); - assert_eq!(s.st_atime, 1000); - assert_eq!(s.st_atime_nsec, 500_000_000); - assert_eq!(s.st_mtime, 2000); - assert_eq!(s.st_mtime_nsec, 0); - assert_eq!(s.st_ctime, 3000); - assert_eq!(s.st_ctime_nsec, 123_456_789); - } - - #[def_test] - fn test_kstat_to_statx() { - let kstat = Kstat { - dev: (5u64 << 32) | 10, - ino: 200, - nlink: 2, - mode: 0o644, - uid: 500, - gid: 500, - size: 8192, - blksize: 4096, - blocks: 16, - rdev: DeviceId::default(), - atime: Duration::new(100, 999_999_999), - mtime: Duration::new(200, 0), - ctime: Duration::new(300, 1), - }; - - let sx: statx = kstat.into(); - assert_eq!(sx.stx_ino, 200); - assert_eq!(sx.stx_nlink, 2); - assert_eq!(sx.stx_mode, 0o644); - assert_eq!(sx.stx_uid, 500); - assert_eq!(sx.stx_gid, 500); - assert_eq!(sx.stx_size, 8192); - assert_eq!(sx.stx_blksize, 4096); - assert_eq!(sx.stx_blocks, 16); - assert_eq!(sx.stx_dev_major, 5); - assert_eq!(sx.stx_dev_minor, 10); - assert_eq!(sx.stx_atime.tv_sec, 100); - assert_eq!(sx.stx_atime.tv_nsec, 999_999_999); - assert_eq!(sx.stx_mtime.tv_sec, 200); - assert_eq!(sx.stx_mtime.tv_nsec, 0); - assert_eq!(sx.stx_ctime.tv_sec, 300); - assert_eq!(sx.stx_ctime.tv_nsec, 1); - } - - #[def_test] - fn test_kstat_default_to_stat_zeroed() { - let s: stat = Kstat::default().into(); - assert_eq!(s.st_dev, 0); - assert_eq!(s.st_size, 0); - assert_eq!(s.st_blksize, 4096); - assert_eq!(s.st_ino, 1); - assert_eq!(s.st_nlink, 1); - } -} - -/// Close all open file descriptors for the current process. -/// -/// This must be called when a process exits, so that pipe write ends and other -/// resources are properly released. Without this, parent processes blocking on -/// pipe reads will never receive EOF. -pub fn close_all_fds() { - // CLONE_FILES may share the same fd table across multiple tasks/processes. - // In that case, an exiting sharer must not clear the whole table, or other - // live sharers (including the parent) will lose stdout/stderr unexpectedly. - if Arc::strong_count(&FD_TABLE) > 1 { - return; - } - - let mut table = FD_TABLE.write(); - let ids: alloc::vec::Vec = table.ids().collect(); - let mut removed = alloc::vec::Vec::with_capacity(ids.len()); - for id in ids { - match table.remove(id) { - Some(fd) => removed.push(fd), - None => warn!("close_all_fds: fd {} disappeared during close sweep", id), - } - } - drop(table); - - // Drop removed descriptors after releasing FD_TABLE lock to avoid - // lock re-entry or side effects from destructor paths. - drop(removed); -} - -pub fn add_stdio(fd_table: &mut FlattenObjects) -> KResult<()> { +pub fn add_stdio(fd_table: &mut FdTable) -> KResult<()> { assert_eq!(fd_table.count(), 0); let cx = FS_CONTEXT.lock(); let open = |options: &mut OpenOptions, flags| { diff --git a/core/kservices/src/file/pidfd.rs b/core/kservices/src/file/pidfd.rs index 1ec43d94..e2f9558e 100644 --- a/core/kservices/src/file/pidfd.rs +++ b/core/kservices/src/file/pidfd.rs @@ -8,9 +8,9 @@ use alloc::{ }; use core::task::Context; -use kcore::task::ProcessData; use kerrno::{KError, KResult}; use kpoll::{IoEvents, PollSet, Pollable}; +use kthread::ProcessState; use crate::file::FileLike; @@ -19,25 +19,25 @@ use crate::file::FileLike; /// A PidFd represents a reference to a process and can be used to monitor /// when the process exits. It uses a weak reference to avoid preventing process cleanup. pub struct PidFd { - /// Weak reference to the process data to avoid keeping the process alive - proc_data: Weak, + /// Weak reference to the process state to avoid keeping the process alive + proc_state: Weak, /// Event notification set for process exit events exit_event: Arc, } impl PidFd { /// Creates a new process file descriptor for the given process. - pub fn new(proc_data: &Arc) -> Self { + pub fn new(proc_state: &Arc) -> Self { Self { - proc_data: Arc::downgrade(proc_data), - exit_event: proc_data.exit_event.clone(), + proc_state: Arc::downgrade(proc_state), + exit_event: proc_state.exit_event().clone(), } } - /// Retrieves the process data if the process is still alive. + /// Retrieves the process state if the process is still alive. /// /// Returns `NoSuchProcess` if the process has already exited. - pub fn process_data(&self) -> KResult> { - self.proc_data.upgrade().ok_or(KError::NoSuchProcess) + pub fn process_state(&self) -> KResult> { + self.proc_state.upgrade().ok_or(KError::NoSuchProcess) } } impl FileLike for PidFd { @@ -51,7 +51,7 @@ impl Pollable for PidFd { /// Polls for readable events (set to true when process is still alive). fn poll(&self) -> IoEvents { let mut events = IoEvents::empty(); - events.set(IoEvents::IN, self.proc_data.strong_count() > 0); + events.set(IoEvents::IN, self.proc_state.strong_count() > 0); events } diff --git a/core/kservices/src/file/pipe.rs b/core/kservices/src/file/pipe.rs index 9a65a501..a1fee328 100644 --- a/core/kservices/src/file/pipe.rs +++ b/core/kservices/src/file/pipe.rs @@ -9,15 +9,14 @@ use core::{ task::Context, }; -use kcore::task::{AsThread, send_signal_to_process}; use kerrno::{KError, KResult}; use kpoll::{IoEvents, PollSet, Pollable}; use ksignal::{SignalInfo, Signo}; use ksync::Mutex; use ktask::{ - current, future::{block_on, poll_io}, }; +use kthread::send_signal_to_process; use linux_raw_sys::{ general::{O_RDONLY, O_WRONLY, S_IFIFO}, ioctl::FIONREAD, @@ -129,9 +128,8 @@ impl Pipe { /// Sends SIGPIPE signal to the current process. fn raise_pipe() { - let curr = current(); send_signal_to_process( - curr.as_thread().proc_data.proc.pid(), + kthread::current_thread().pid(), Some(SignalInfo::new_kernel(Signo::SIGPIPE)), ) .expect("Failed to send SIGPIPE"); diff --git a/core/kservices/src/file/signalfd.rs b/core/kservices/src/file/signalfd.rs index 20c8514f..4dbfd895 100644 --- a/core/kservices/src/file/signalfd.rs +++ b/core/kservices/src/file/signalfd.rs @@ -9,13 +9,11 @@ use core::{ task::Context, }; -use kcore::task::AsThread; use kerrno::{KError, KResult}; use kpoll::{IoEvents, PollSet, Pollable}; use ksignal::{SignalInfo, SignalSet}; use ksync::RwLock; use ktask::{ - current, future::{block_on, poll_io}, }; use zerocopy::{Immutable, IntoBytes}; @@ -108,8 +106,7 @@ impl Signalfd { /// Check if there are any pending signals matching the mask fn has_pending_signals(&self) -> bool { let mask = self.mask(); - let curr = current(); - let signal = &curr.as_thread().signal; + let signal = &kthread::current_thread().signal; let pending = signal.pending(); !(pending & mask).is_empty() } @@ -117,8 +114,7 @@ impl Signalfd { /// Dequeue a signal matching the mask fn dequeue_signal(&self) -> Option { let mask = self.mask(); - let curr = current(); - let signal = &curr.as_thread().signal; + let signal = &kthread::current_thread().signal; signal.dequeue_signal(&mask) } } diff --git a/core/kservices/src/lib.rs b/core/kservices/src/lib.rs index 94ec2e31..0dd744a5 100644 --- a/core/kservices/src/lib.rs +++ b/core/kservices/src/lib.rs @@ -43,5 +43,5 @@ pub fn init() { }); info!("Initialize alarm..."); - kcore::time::spawn_alarm_task(); + kthread::spawn_alarm_task(); } diff --git a/core/kservices/src/mm.rs b/core/kservices/src/mm.rs index 7be91106..05cd49a5 100644 --- a/core/kservices/src/mm.rs +++ b/core/kservices/src/mm.rs @@ -13,14 +13,15 @@ use core::{ ptr, slice, str, }; -use kcore::{mm::access_user_memory, task::AsThread}; +use kcore::mm::access_user_memory; use kerrno::{KError, KResult}; use khal::{ paging::MappingFlags, trap::{PAGE_FAULT, register_trap_handler}, }; use kio::prelude::*; -use ktask::{current, current_may_uninit}; +use ktask::current_may_uninit; +use kthread::AsThread; use memaddr::{MemoryAddr, PAGE_SIZE_4K, VirtAddr}; use osvm::{load_vec, load_vec_until_null, read_vm_mem, write_vm_mem}; @@ -31,8 +32,8 @@ fn check_region(start: VirtAddr, layout: Layout, access_flags: MappingFlags) -> return Err(KError::BadAddress); } - let curr = current(); - let mut aspace = curr.as_thread().proc_data.aspace.lock(); + let current_thread = kthread::current_thread(); + let mut aspace = current_thread.process_state().address_space().lock(); if !aspace.can_access_range(start, layout.size(), access_flags) { return Err(KError::BadAddress); @@ -75,8 +76,8 @@ fn check_null_terminated( // TODO: this is inefficient, but we have to do this instead of // querying the page table since the page might has not been // allocated yet. - let curr = current(); - let aspace = curr.as_thread().proc_data.aspace.lock(); + let current_thread = kthread::current_thread(); + let aspace = current_thread.process_state().address_space().lock(); if !aspace.can_access_range(page, PAGE_SIZE_4K, access_flags) { return Err(KError::BadAddress); } @@ -269,8 +270,8 @@ fn dispatch_irq_page_fault(vaddr: VirtAddr, access_flags: MappingFlags) -> bool return false; } - thr.proc_data - .aspace + thr.proc_state + .address_space() .lock() .dispatch_irq_page_fault(vaddr, access_flags) } diff --git a/core/kservices/src/signal.rs b/core/kservices/src/signal.rs index 6bfc0a42..f9fb1653 100644 --- a/core/kservices/src/signal.rs +++ b/core/kservices/src/signal.rs @@ -6,11 +6,10 @@ use core::sync::atomic::{AtomicBool, Ordering}; -use kcore::task::{AsThread, Thread}; use kerrno::KResult; use khal::uspace::UserContext; use ksignal::{SignalOSAction, SignalSet}; -use ktask::current; +use kthread::Thread; use crate::task::do_exit; @@ -64,8 +63,7 @@ pub fn with_replacen_blocked( blocked: Option, f: impl FnOnce() -> KResult, ) -> KResult { - let curr = current(); - let sig = &curr.as_thread().signal; + let sig = &kthread::current_thread().signal; let old_blocked = blocked.map(|set| sig.set_blocked(set)); f().inspect(|_| { diff --git a/core/kservices/src/task.rs b/core/kservices/src/task.rs index 1cfaf015..0c56241b 100644 --- a/core/kservices/src/task.rs +++ b/core/kservices/src/task.rs @@ -7,19 +7,15 @@ use core::{ffi::c_long, sync::atomic::Ordering}; use bytemuck::AnyBitPattern; -use kcore::{ - futex::FutexKey, - task::{ - AsThread, get_process_data, get_task, send_signal_to_process, send_signal_to_thread, - set_timer_state, - }, - time::TimerState, -}; use kerrno::{KError, KResult}; use khal::uspace::{ExceptionKind, ReturnReason, UserContext}; use kprocess::Pid; use ksignal::{SignalInfo, Signo}; use ktask::{TaskInner, current}; +use kthread::{ + TimerState, current_futex_key, get_process_state, get_task, send_signal_to_process, + send_signal_to_thread, set_timer_state, +}; use linux_raw_sys::general::ROBUST_LIST_LIMIT; use osvm::{VirtMutPtr, VirtPtr}; use posix_ipc::SHM_MANAGER; @@ -43,8 +39,8 @@ pub fn new_user_task( info!("Enter user space: ip={:#x}, sp={:#x}", uctx.ip(), uctx.sp()); - let thr = curr.as_thread(); - while !thr.pending_exit() { + let thr = kthread::current_thread(); + while !thr.is_exiting() { let reason = uctx.run(); set_timer_state(&curr, TimerState::Kernel); @@ -53,14 +49,14 @@ pub fn new_user_task( ReturnReason::Syscall => dispatch_syscall(&mut uctx), ReturnReason::PageFault(addr, flags) => { if !thr - .proc_data - .aspace + .proc_state + .address_space() .lock() .dispatch_irq_page_fault(addr, flags) { info!( "{:?}: segmentation fault at {:#x} {:?}", - thr.proc_data.proc, addr, flags + thr.proc_state.proc, addr, flags ); raise_signal_fatal(SignalInfo::new_kernel(Signo::SIGSEGV)) .expect("Failed to send SIGSEGV"); @@ -93,7 +89,7 @@ pub fn new_user_task( } if !unblock_next_signal() { - while check_signals(thr, &mut uctx, None) {} + while check_signals(&thr, &mut uctx, None) {} } set_timer_state(&curr, TimerState::User); @@ -131,10 +127,9 @@ fn dispatch_irq_futex_death(entry: *mut RobustList, offset: i64) -> KResult<()> .checked_add_signed(offset) .ok_or(KError::InvalidInput)?; let address: usize = address.try_into().map_err(|_| KError::InvalidInput)?; - let key = FutexKey::new_current(address); + let key = current_futex_key(address); - let curr = current(); - let futex_table = curr.as_thread().proc_data.futex_table_for(&key); + let futex_table = kthread::current_thread().proc_state.futex_table_for(&key); let Some(futex) = futex_table.get(&key) else { return Ok(()); @@ -185,14 +180,14 @@ pub fn exit_robust_list(head: *const RobustListHead) { /// Exit the current thread or process group and perform cleanup. pub fn do_exit(exit_code: i32, group_exit: bool) { let curr = current(); - let thr = curr.as_thread(); + let thr = kthread::current_thread(); info!("{} exit with code: {}", curr.id_name(), exit_code); let clear_child_tid = thr.clear_child_tid() as *mut u32; if clear_child_tid.write_vm(0).is_ok() { - let key = FutexKey::new_current(clear_child_tid as usize); - let table = thr.proc_data.futex_table_for(&key); + let key = current_futex_key(clear_child_tid as usize); + let table = thr.proc_state.futex_table_for(&key); let guard = table.get(&key); if let Some(futex) = guard { futex.wq.wake(1, u32::MAX); @@ -204,7 +199,7 @@ pub fn do_exit(exit_code: i32, group_exit: bool) { exit_robust_list(head); } - let process = &thr.proc_data.proc; + let process = &thr.proc_state.proc; if process.exit_thread(curr.id().as_u64() as Pid, exit_code) { // Close all file descriptors before marking the process as exited. // This ensures pipe write ends and other resources are properly released, @@ -213,14 +208,14 @@ pub fn do_exit(exit_code: i32, group_exit: bool) { process.exit(); if let Some(parent) = process.parent() { - if let Some(signo) = thr.proc_data.exit_signal { + if let Some(signo) = thr.proc_state.exit_signal() { let _ = send_signal_to_process(parent.pid(), Some(SignalInfo::new_kernel(signo))); } - if let Ok(data) = get_process_data(parent.pid()) { - data.child_exit_event.wake(); + if let Ok(data) = get_process_state(parent.pid()) { + data.child_exit_event().wake(); } } - thr.proc_data.exit_event.wake(); + thr.proc_state.exit_event().wake(); SHM_MANAGER.lock().clear_proc_shm(process.pid()); } @@ -236,12 +231,11 @@ pub fn do_exit(exit_code: i32, group_exit: bool) { /// Sends a fatal signal to the current process. pub fn raise_signal_fatal(sig: SignalInfo) -> KResult<()> { - let curr = current(); - let proc_data = &curr.as_thread().proc_data; + let proc_state = &kthread::current_thread().proc_state; let signo = sig.signo(); info!("Send fatal signal {signo:?} to the current process"); - if let Some(tid) = proc_data.signal.send_signal(sig) + if let Some(tid) = proc_state.signal.send_signal(sig) && let Ok(task) = get_task(tid) { task.interrupt(); diff --git a/core/kservices/src/terminal/job.rs b/core/kservices/src/terminal/job.rs index 9f01abb8..e4b5af90 100644 --- a/core/kservices/src/terminal/job.rs +++ b/core/kservices/src/terminal/job.rs @@ -5,12 +5,10 @@ use alloc::sync::{Arc, Weak}; use core::task::Context; -use kcore::task::AsThread; use kerrno::{KResult, k_bail}; use kpoll::{IoEvents, PollSet, Pollable}; use kprocess::{ProcessGroup, Session}; use kspin::SpinNoIrq; -use ktask::current; pub struct JobControl { foreground: SpinNoIrq>, @@ -38,7 +36,7 @@ impl JobControl { self.foreground .lock() .upgrade() - .is_none_or(|pg| Arc::ptr_eq(¤t().as_thread().proc_data.proc.group(), &pg)) + .is_none_or(|pg| Arc::ptr_eq(&kthread::current_thread().proc_state.proc.group(), &pg)) } /// Get the current foreground process group diff --git a/core/kservices/src/terminal/ldisc.rs b/core/kservices/src/terminal/ldisc.rs index 402ab662..385917ad 100644 --- a/core/kservices/src/terminal/ldisc.rs +++ b/core/kservices/src/terminal/ldisc.rs @@ -10,11 +10,11 @@ use core::{ task::{Poll, Waker}, }; -use kcore::task::send_signal_to_process_group; use kerrno::{KError, KResult}; use kpoll::PollSet; use ksignal::SignalInfo; use ktask::future::block_on; +use kthread::send_signal_to_process_group; use linux_raw_sys::general::{ ECHOCTL, ECHOK, ICRNL, IGNCR, ISIG, VEOF, VERASE, VKILL, VMIN, VTIME, }; diff --git a/core/kservices/src/unittest_task.rs b/core/kservices/src/unittest_task.rs index a32bd445..28cb5bb0 100644 --- a/core/kservices/src/unittest_task.rs +++ b/core/kservices/src/unittest_task.rs @@ -9,7 +9,6 @@ use alloc::{string::ToString, sync::Arc, vec}; use core::sync::atomic::{AtomicU32, Ordering}; -use kcore::task::{ProcessData, Thread}; use kcred::Credentials; use kerrno::KResult; use kprocess::Pid; @@ -19,6 +18,7 @@ use ksync::{ spin::{NoPreempt, SpinNoIrq}, }; use ktask::{KTaskExt, TaskExt, current}; +use kthread::{ProcessState, ProcessStateConfig, Thread}; use unittest::{TestDescriptor, TestResult}; use unittest_support::TestUserBuffer; @@ -54,7 +54,7 @@ impl InstalledTestThread { let proc = kprocess::Process::new_init(pid); proc.add_thread(tid); - let proc_data = ProcessData::new( + let proc_state = ProcessState::new( proc, "[unittest-user]".to_string(), Arc::new(vec![]), @@ -62,11 +62,16 @@ impl InstalledTestThread { Arc::new(SpinNoIrq::new(SignalActions::default())), None, Credentials::root(), + ProcessStateConfig { + user_heap_base: kcore::config::USER_HEAP_BASE, + user_stack_size: kcore::config::USER_STACK_SIZE, + signal_trampoline: kcore::config::SIGNAL_TRAMPOLINE, + }, ); - let thr = Thread::new(tid, proc_data); + let thr = Thread::new(tid, proc_state); init_thread(&thr); - let page_table_root = thr.proc_data.aspace.lock().page_table_root(); + let page_table_root = thr.proc_state.address_space().lock().page_table_root(); let task_ptr = current_task_ptr(); let previous_page_table_root = karch::read_user_page_table(); diff --git a/core/kservices/src/vfs/dev/dice.rs b/core/kservices/src/vfs/dev/dice.rs index a21ef220..3bb4a060 100644 --- a/core/kservices/src/vfs/dev/dice.rs +++ b/core/kservices/src/vfs/dev/dice.rs @@ -89,12 +89,12 @@ impl DeviceOps for DiceNodeInfo { fn get_process_hash() -> KResult> { use alloc::format; - use kcore::task::AsThread; use kfs::FS_CONTEXT; use ktask::current; + use kthread::AsThread; use mbedtls::hash::{Md, Type}; - let pid = current().as_thread().proc_data.proc.pid(); + let pid = kthread::current_thread().pid(); let proc_exe_path = format!("/proc/{}/exe", pid); let fs = FS_CONTEXT.lock(); let data = fs.read(proc_exe_path).unwrap(); diff --git a/core/kservices/src/vfs/dev/memtrack.rs b/core/kservices/src/vfs/dev/memtrack.rs index 4d79ed20..6522d19f 100644 --- a/core/kservices/src/vfs/dev/memtrack.rs +++ b/core/kservices/src/vfs/dev/memtrack.rs @@ -12,11 +12,8 @@ use core::{ use backtrace::Backtrace; use fs_ng_vfs::{NodeFlags, VfsResult}; -use kcore::{ - mm::clear_elf_cache, - task::{cleanup_task_tables, tasks}, - vfs::DeviceOps, -}; +use kcore::{mm::clear_elf_cache, vfs::DeviceOps}; +use kthread::{cleanup_task_tables, tasks}; static STAMPED_GENERATION: AtomicU64 = AtomicU64::new(0); @@ -52,8 +49,8 @@ impl MemoryCategory { "kcore::mm::ElfLoader::load" => { return Some("elf cache"); } - "kcore::task::ProcessData::new" => { - return Some("process data"); + "kcore::task::ProcessState::new" => { + return Some("process state"); } "kprocess::process::Process::new" => { return Some("process"); diff --git a/core/kservices/src/vfs/dev/tty.rs b/core/kservices/src/vfs/dev/tty.rs index 30a98762..e5ab0b58 100644 --- a/core/kservices/src/vfs/dev/tty.rs +++ b/core/kservices/src/vfs/dev/tty.rs @@ -6,15 +6,11 @@ use alloc::sync::{Arc, Weak}; use core::{any::Any, ops::Deref, sync::atomic::Ordering, task::Context}; use fs_ng_vfs::NodeFlags; -use kcore::{ - task::AsThread, - vfs::{DeviceOps, SimpleFs}, -}; +use kcore::vfs::{DeviceOps, SimpleFs}; use kerrno::{KError, KResult}; use kpoll::{IoEvents, Pollable}; use kprocess::Process; use ksync::Mutex; -use ktask::current; use osvm::{VirtMutPtr, VirtPtr}; use crate::terminal::{ @@ -134,10 +130,9 @@ impl DeviceOps for Tty { (arg as *mut u32).write_vm(foreground.pgid())?; } TIOCSPGRP => { - let curr = current(); self.terminal .job_control - .set_foreground(&curr.as_thread().proc_data.proc.group())?; + .set_foreground(&kthread::current_thread().proc_state.proc.group())?; } TIOCGWINSZ => { (arg as *mut WindowSize).write_vm(*self.terminal.window_size.lock())?; @@ -153,12 +148,11 @@ impl DeviceOps for Tty { self.this .upgrade() .unwrap() - .bind_to(¤t().as_thread().proc_data.proc)?; + .bind_to(&kthread::current_thread().proc_state.proc)?; } TIOCNOTTY => { - if current() - .as_thread() - .proc_data + if kthread::current_thread() + .proc_state .proc .group() .session() diff --git a/core/kservices/src/vfs/mod.rs b/core/kservices/src/vfs/mod.rs index 4e1c18b6..ea9bdc28 100644 --- a/core/kservices/src/vfs/mod.rs +++ b/core/kservices/src/vfs/mod.rs @@ -13,19 +13,21 @@ use fs_ng_vfs::{ Filesystem, NodePermission, ST_NODEV, ST_NOEXEC, ST_NOSUID, ST_RELATIME, VfsError, VfsResult, path::{Path, PathBuf}, }; -use kcore::task::AsThread; pub use kcore::vfs::{Device, DeviceOps, DirMapping, SimpleFs}; use kerrno::{LinuxError, LinuxResult}; use kfs::{FS_CONTEXT, FsContext}; use ktask::KtaskRef; +use kthread::AsThread; use procfs::ProcFsHooks; pub use tmp::MemoryFs; const DIR_PERMISSION: NodePermission = NodePermission::from_bits_truncate(0o755); fn procfs_fd_ids(task: &KtaskRef) -> Vec { - crate::file::FD_TABLE - .scope(&task.as_thread().proc_data.scope.read()) + task.as_thread() + .proc_state + .resources + .fd_table() .read() .ids() .map(|id| id as u32) @@ -33,8 +35,10 @@ fn procfs_fd_ids(task: &KtaskRef) -> Vec { } fn procfs_fd_path(task: &KtaskRef, fd: u32) -> VfsResult { - crate::file::FD_TABLE - .scope(&task.as_thread().proc_data.scope.read()) + task.as_thread() + .proc_state + .resources + .fd_table() .read() .get(fd as _) .ok_or(VfsError::NotFound) diff --git a/core/ksyscall/Cargo.toml b/core/ksyscall/Cargo.toml index fa772789..93ce61c9 100644 --- a/core/ksyscall/Cargo.toml +++ b/core/ksyscall/Cargo.toml @@ -13,10 +13,12 @@ vsock = ["knet/vsock", "kservices/vsock"] tee = [ "dep:tee_kernel", "dep:tee_task_iface", + "kthread/tee", "kservices/tee", ] [dependencies] +kfd.workspace = true kservices = { workspace = true } kbuild_config = { workspace = true } kerrno.workspace = true @@ -32,6 +34,7 @@ bytemuck.workspace = true kspin.workspace = true linux-raw-sys = { workspace = true, features = ["ioctl", "loop_device"] } kprocess.workspace = true +kthread.workspace = true ksignal.workspace = true osvm.workspace = true linux_sysno.workspace = true @@ -42,7 +45,10 @@ posix-credentials.workspace = true posix-fs.workspace = true posix-ipc.workspace = true posix-mm.workspace = true +posix-process.workspace = true posix-sched.workspace = true +posix-signal.workspace = true +posix-sync.workspace = true posix-types.workspace = true num_enum = { version = "0.7", default-features = false } diff --git a/core/ksyscall/src/dispatch.rs b/core/ksyscall/src/dispatch.rs index 978573be..d4977540 100644 --- a/core/ksyscall/src/dispatch.rs +++ b/core/ksyscall/src/dispatch.rs @@ -14,7 +14,7 @@ //! - `net`: Network operations //! - `posix-credentials`: POSIX credential operations //! - `resources`: Resource limits and usage -//! - `signal`: Signal handling +//! - `posix-signal`: Signal handling //! - `sync`: Synchronization primitives //! - `sys`: System information and control //! - `task`: Process and thread management @@ -26,8 +26,10 @@ use linux_sysno::Sysno; use posix_credentials::*; use posix_ipc::*; use posix_mm::*; +use posix_process::*; +use posix_signal::*; -use crate::{fs::*, io_mpx::*, net::*, resources::*, signal::*, sync::*, sys::*, task::*, time::*}; +use crate::{fs::*, io_mpx::*, net::*, resources::*, sync::*, sys::*, task::*, time::*}; /// Dispatches a syscall from the given user context. pub fn dispatch_irq_syscall(uctx: &mut UserContext) { @@ -493,17 +495,17 @@ pub fn dispatch_irq_syscall(uctx: &mut UserContext) { ), Sysno::sigaltstack => sys_sigaltstack(uctx.arg0() as _, uctx.arg1() as _), Sysno::futex => sys_futex( - uctx.arg0() as _, + uctx.arg0().into(), uctx.arg1() as _, uctx.arg2() as _, uctx.arg3() as _, - uctx.arg4() as _, + uctx.arg4().into(), uctx.arg5() as _, ), Sysno::get_robust_list => { - sys_get_robust_list(uctx.arg0() as _, uctx.arg1() as _, uctx.arg2() as _) + sys_get_robust_list(uctx.arg0() as _, uctx.arg1().into(), uctx.arg2().into()) } - Sysno::set_robust_list => sys_set_robust_list(uctx.arg0() as _, uctx.arg1() as _), + Sysno::set_robust_list => sys_set_robust_list(uctx.arg0().into(), uctx.arg1() as _), // sys Sysno::getuid => sys_getuid(), diff --git a/core/ksyscall/src/fs/event.rs b/core/ksyscall/src/fs/event.rs index 1f93b087..d00ba177 100644 --- a/core/ksyscall/src/fs/event.rs +++ b/core/ksyscall/src/fs/event.rs @@ -10,7 +10,8 @@ use bitflags::bitflags; use kerrno::{KError, KResult}; -use kservices::file::{FileLike, add_file_like, event::EventFd}; +use kfd::{FileLike, add_file_like}; +use kservices::file::event::EventFd; use linux_raw_sys::general::{EFD_CLOEXEC, EFD_NONBLOCK, EFD_SEMAPHORE}; bitflags! { diff --git a/core/ksyscall/src/fs/pidfd.rs b/core/ksyscall/src/fs/pidfd.rs index 4c618ff7..a3a74e30 100644 --- a/core/ksyscall/src/fs/pidfd.rs +++ b/core/ksyscall/src/fs/pidfd.rs @@ -9,14 +9,12 @@ //! - Pidfd operations (pidfd_getfd, pidfd_send_signal, etc.) //! - Process monitoring through pidfds -use kcore::task::{get_process_data, send_signal_to_process}; use kerrno::{KError, KResult}; +use kfd::{FileLike, add_file_like}; +use kservices::file::PidFd; use ksignal::SignalInfo; - -use crate::{ - file::{FD_TABLE, FileLike, PidFd, add_file_like}, - signal::make_queue_signal_info, -}; +use kthread::{get_process_state, send_signal_to_process}; +use posix_signal::make_queue_signal_info; /// Create a process file descriptor (pidfd) for the specified process /// @@ -31,8 +29,8 @@ pub fn sys_pidfd_open(pid: u32, flags: u32) -> KResult { return Err(KError::InvalidInput); } - // Get the process data for the specified PID - let task = get_process_data(pid)?; + // Get the process state for the specified PID + let task = get_process_state(pid)?; // Create a new pidfd object wrapping the process let fd = PidFd::new(&task); @@ -50,11 +48,12 @@ pub fn sys_pidfd_getfd(pidfd: i32, target_fd: i32, flags: u32) -> KResult // Get the pidfd object and validate it let pidfd = PidFd::from_fd(pidfd)?; - // Get the process data that this pidfd refers to - let proc_data = pidfd.process_data()?; + // Get the process state that this pidfd refers to + let proc_state = pidfd.process_state()?; // Access the target process's file descriptor table within its scope - FD_TABLE - .scope(&proc_data.scope.read()) + proc_state + .resources + .fd_table() .read() // Get the file descriptor at the specified index .get(target_fd as usize) @@ -84,7 +83,7 @@ pub fn sys_pidfd_send_signal( // Get the pidfd object and retrieve the process it refers to let pidfd = PidFd::from_fd(pidfd)?; - let pid = pidfd.process_data()?.proc.pid(); + let pid = pidfd.process_state()?.proc.pid(); // Create signal info from user-provided data and send the signal let sig = make_queue_signal_info(pid, signo, sig)?; diff --git a/core/ksyscall/src/fs/signalfd.rs b/core/ksyscall/src/fs/signalfd.rs index c81da071..4ad5db5c 100644 --- a/core/ksyscall/src/fs/signalfd.rs +++ b/core/ksyscall/src/fs/signalfd.rs @@ -14,10 +14,10 @@ use kerrno::{KError, KResult}; use ksignal::SignalSet; use linux_raw_sys::general::{O_CLOEXEC, O_NONBLOCK}; use osvm::VirtPtr; +use posix_signal::check_sigset_size; use crate::{ file::{FileLike, add_file_like, signalfd::Signalfd}, - signal::check_sigset_size, }; // SFD flag definitions (if not available in linux_raw_sys) diff --git a/core/ksyscall/src/fs/timerfd.rs b/core/ksyscall/src/fs/timerfd.rs index 64df0eda..e2c84755 100644 --- a/core/ksyscall/src/fs/timerfd.rs +++ b/core/ksyscall/src/fs/timerfd.rs @@ -10,7 +10,8 @@ //! - Timer query (timerfd_gettime) use kerrno::{KError, KResult}; -use kservices::file::{FileLike, add_file_like, timerfd::TimerFd}; +use kfd::{FileLike, add_file_like}; +use kservices::file::timerfd::TimerFd; use linux_raw_sys::general::{ CLOCK_BOOTTIME, CLOCK_MONOTONIC, CLOCK_REALTIME, TFD_CLOEXEC, TFD_NONBLOCK, TFD_TIMER_ABSTIME, itimerspec, timespec, diff --git a/core/ksyscall/src/io_mpx/epoll.rs b/core/ksyscall/src/io_mpx/epoll.rs index bee2a76b..a2f8a1ea 100644 --- a/core/ksyscall/src/io_mpx/epoll.rs +++ b/core/ksyscall/src/io_mpx/epoll.rs @@ -25,6 +25,7 @@ use ktask::future::{self, block_on, poll_io}; use linux_raw_sys::general::{ EPOLL_CLOEXEC, EPOLL_CTL_ADD, EPOLL_CTL_DEL, EPOLL_CTL_MOD, epoll_event, timespec, }; +use posix_signal::check_sigset_size; use posix_types::TimeValueLike; use crate::{ @@ -32,7 +33,6 @@ use crate::{ FileLike, epoll::{Epoll, EpollEvent, EpollFlags}, }, - signal::check_sigset_size, }; bitflags! { diff --git a/core/ksyscall/src/io_mpx/mod.rs b/core/ksyscall/src/io_mpx/mod.rs index af0666b1..a1863d2d 100644 --- a/core/ksyscall/src/io_mpx/mod.rs +++ b/core/ksyscall/src/io_mpx/mod.rs @@ -18,8 +18,8 @@ mod select; use alloc::{sync::Arc, vec::Vec}; use core::task::Context; +use kfd::FileLike; use kpoll::{IoEvents, Pollable}; -use kservices::file::FileLike; pub use self::{epoll::*, poll::*, select::*}; diff --git a/core/ksyscall/src/io_mpx/poll.rs b/core/ksyscall/src/io_mpx/poll.rs index edcca2a0..22b48bd4 100644 --- a/core/ksyscall/src/io_mpx/poll.rs +++ b/core/ksyscall/src/io_mpx/poll.rs @@ -12,6 +12,7 @@ use alloc::vec::Vec; use kerrno::{KError, KResult}; +use kfd::get_file_like; use khal::time::TimeValue; use kpoll::IoEvents; use kservices::{ @@ -22,10 +23,10 @@ use kservices::{ use ksignal::SignalSet; use ktask::future::{self, block_on, poll_io}; use linux_raw_sys::general::{POLLNVAL, pollfd, timespec}; +use posix_signal::check_sigset_size; use posix_types::TimeValueLike; use super::FdPollSet; -use crate::{file::get_file_like, signal::check_sigset_size}; /// Monitor multiple file descriptors for I/O events with optional timeout fn do_poll( diff --git a/core/ksyscall/src/io_mpx/select.rs b/core/ksyscall/src/io_mpx/select.rs index 89ef90f8..e30a6eec 100644 --- a/core/ksyscall/src/io_mpx/select.rs +++ b/core/ksyscall/src/io_mpx/select.rs @@ -14,6 +14,7 @@ use core::{fmt, time::Duration}; use bytemuck::AnyBitPattern; use kerrno::{KError, KResult}; +use kfd::current_fd_table; use kpoll::IoEvents; use kservices::{ mm::{UserConstPtr, UserPtr}, @@ -24,10 +25,10 @@ use ksignal::SignalSet; use ktask::future::{self, block_on, poll_io}; use linux_raw_sys::general::*; use osvm::{VirtMutPtr, VirtPtr}; +use posix_signal::check_sigset_size; use posix_types::TimeValueLike; use super::FdPollSet; -use crate::{file::FD_TABLE, signal::check_sigset_size}; const FD_SETSIZE: usize = __FD_SETSIZE as usize; @@ -146,7 +147,8 @@ fn do_select( {except_fds:?}] timeout: {timeout:?}" ); - let fd_table = FD_TABLE.read(); + let fd_table = current_fd_table(); + let fd_table = fd_table.read(); let mut fds = Vec::with_capacity(nfds); let mut fd_indices = Vec::with_capacity(nfds); for fd in 0..nfds { diff --git a/core/ksyscall/src/lib.rs b/core/ksyscall/src/lib.rs index 59949012..b6e75fd3 100644 --- a/core/ksyscall/src/lib.rs +++ b/core/ksyscall/src/lib.rs @@ -19,7 +19,6 @@ mod fs; mod io_mpx; mod net; mod resources; -mod signal; mod sync; mod sys; mod task; diff --git a/core/ksyscall/src/net/cmsg.rs b/core/ksyscall/src/net/cmsg.rs index 6db77814..96d52191 100644 --- a/core/ksyscall/src/net/cmsg.rs +++ b/core/ksyscall/src/net/cmsg.rs @@ -12,6 +12,7 @@ use core::{mem::size_of, net::SocketAddr, ptr}; use bytemuck::{NoUninit, bytes_of}; use kerrno::{KError, KResult, LinuxError}; +use kfd::{FileLike, get_file_like}; use knet::UdpRecvError; use kservices::mm::{UserConstPtr, UserPtr}; use linux_raw_sys::net::{ @@ -19,8 +20,6 @@ use linux_raw_sys::net::{ sockaddr_in, }; -use crate::file::{FileLike, get_file_like}; - #[repr(C)] #[derive(Clone, Copy)] struct SockExtendedErr { diff --git a/core/ksyscall/src/net/opt.rs b/core/ksyscall/src/net/opt.rs index f824479d..71a307ec 100644 --- a/core/ksyscall/src/net/opt.rs +++ b/core/ksyscall/src/net/opt.rs @@ -10,12 +10,14 @@ //! - Socket-level, IP-level, TCP-level, and other protocol options use kerrno::{KError, KResult, LinuxError}; +use kfd::FileLike; use knet::options::{Configurable, GetSocketOption, SetSocketOption}; -use kservices::mm::{UserConstPtr, UserPtr}; +use kservices::{ + file::Socket, + mm::{UserConstPtr, UserPtr}, +}; use linux_raw_sys::net::socklen_t; -use crate::file::{FileLike, Socket}; - const PROTO_TCP: u32 = linux_raw_sys::net::IPPROTO_TCP as u32; const PROTO_IP: u32 = linux_raw_sys::net::IPPROTO_IP as u32; diff --git a/core/ksyscall/src/net/socket.rs b/core/ksyscall/src/net/socket.rs index 4a6e4c2c..acc413e6 100644 --- a/core/ksyscall/src/net/socket.rs +++ b/core/ksyscall/src/net/socket.rs @@ -12,7 +12,6 @@ use alloc::boxed::Box; -use kcore::task::AsThread; use kerrno::{KError, KResult, LinuxError}; #[cfg(feature = "vsock")] use knet::vsock::{VsockSocket, VsockStreamTransport}; @@ -25,7 +24,6 @@ use knet::{ unix::{DgramTransport, StreamTransport, UnixDomainSocket}, }; use kservices::mm::{UserConstPtr, UserPtr}; -use ktask::current; use linux_raw_sys::{ general::{O_CLOEXEC, O_NONBLOCK}, net::{ @@ -45,7 +43,7 @@ pub fn sys_socket(domain: u32, raw_ty: u32, proto: u32) -> KResult { // Extract the type bits (lower 8 bits, ignoring flags like SOCK_CLOEXEC) let ty = raw_ty & 0xFF; - let pid = current().as_thread().proc_data.proc.pid(); + let pid = kthread::current_thread().pid(); // Create the appropriate socket type based on domain and type let socket = match (domain, ty) { (AF_INET, SOCK_STREAM) => { @@ -200,7 +198,7 @@ pub fn sys_socketpair( return Err(KError::from(LinuxError::EAFNOSUPPORT)); } - let pid = current().as_thread().proc_data.proc.pid(); + let pid = kthread::current_thread().pid(); let (sock1, sock2) = match ty { SOCK_STREAM => { let (sock1, sock2) = StreamTransport::new_pair(pid); diff --git a/core/ksyscall/src/resources.rs b/core/ksyscall/src/resources.rs index 5d2e67a2..f5c0d47e 100644 --- a/core/ksyscall/src/resources.rs +++ b/core/ksyscall/src/resources.rs @@ -2,18 +2,14 @@ // Copyright 2025 KylinSoft Co., Ltd. // See LICENSES for license details. -//! Resource limit and usage syscalls. +//! Resource limit syscalls. //! -//! This module provides syscalls for managing resource limits (prlimit64) -//! and retrieving resource usage information (getrusage). -use kcore::task::{AsThread, Thread, get_process_data, get_task}; +//! This module provides the `prlimit64` syscall. use kerrno::{KError, KResult}; -use khal::time::TimeValue; use kprocess::Pid; -use ktask::current; -use linux_raw_sys::general::{__kernel_old_timeval, RLIM_NLIMITS, rlimit64, rusage}; +use kthread::get_process_state; +use linux_raw_sys::general::{RLIM_NLIMITS, rlimit64}; use osvm::{VirtMutPtr, VirtPtr}; -use posix_types::TimeValueLike; /// Get and/or set resource limits for a process pub fn sys_prlimit64( @@ -26,9 +22,9 @@ pub fn sys_prlimit64( return Err(KError::InvalidInput); } - let proc_data = get_process_data(pid)?; + let proc_state = get_process_state(pid)?; if let Some(old_limit) = old_limit.check_non_null() { - let limit = &proc_data.rlim.read()[resource]; + let limit = &proc_state.resources.rlimits.read()[resource]; old_limit.write_vm(rlimit64 { rlim_cur: limit.current, rlim_max: limit.max, @@ -42,7 +38,7 @@ pub fn sys_prlimit64( return Err(KError::InvalidInput); } - let limit = &mut proc_data.rlim.write()[resource]; + let limit = &mut proc_state.resources.rlimits.write()[resource]; if new_limit.rlim_max <= limit.max { limit.max = new_limit.rlim_max; } else { @@ -56,78 +52,3 @@ pub fn sys_prlimit64( Ok(0) } - -#[derive(Default)] -struct Rusage { - utime: TimeValue, - stime: TimeValue, -} - -impl Rusage { - fn from_thread(thread: &Thread) -> Self { - let (utime, stime) = thread.time.borrow().output(); - Self { utime, stime } - } - - fn collate(mut self, other: Rusage) -> Self { - self.utime += other.utime; - self.stime += other.stime; - self - } -} - -impl From for rusage { - fn from(value: Rusage) -> Self { - // FIXME: Zeroable - let mut usage: rusage = unsafe { core::mem::zeroed() }; - usage.ru_utime = __kernel_old_timeval::from_time_value(value.utime); - usage.ru_stime = __kernel_old_timeval::from_time_value(value.stime); - usage - } -} - -/// Get resource usage information for the current process, children, or specific thread -pub fn sys_getrusage(who: i32, usage: *mut rusage) -> KResult { - const RUSAGE_SELF: i32 = linux_raw_sys::general::RUSAGE_SELF as i32; - const RUSAGE_CHILDREN: i32 = linux_raw_sys::general::RUSAGE_CHILDREN; - const RUSAGE_THREAD: i32 = linux_raw_sys::general::RUSAGE_THREAD as i32; - - let curr = current(); - let thr = curr.as_thread(); - - let result = match who { - RUSAGE_SELF => { - thr.proc_data - .proc - .threads() - .into_iter() - .fold(Rusage::default(), |acc, tid| { - if let Ok(task) = get_task(tid) { - acc.collate(Rusage::from_thread(task.as_thread())) - } else { - acc - } - }) - } - RUSAGE_CHILDREN => { - thr.proc_data - .proc - .threads() - .into_iter() - .fold(Rusage::default(), |acc, child| { - if let Ok(task) = get_task(child) - && !curr.ptr_eq(&task) - { - acc.collate(Rusage::from_thread(task.as_thread())) - } else { - acc - } - }) - } - RUSAGE_THREAD => Rusage::from_thread(thr), - _ => return Err(KError::InvalidInput), - }; - usage.write_vm(result.into())?; - - Ok(0) -} diff --git a/core/ksyscall/src/sync/mod.rs b/core/ksyscall/src/sync/mod.rs index 0634320f..47a20c5d 100644 --- a/core/ksyscall/src/sync/mod.rs +++ b/core/ksyscall/src/sync/mod.rs @@ -5,11 +5,11 @@ //! Synchronization and atomic operation syscalls. //! //! This module implements synchronization primitives and memory operations including: -//! - Futex operations (futex, futex2, etc.) //! - Memory barriers (membarrier, etc.) //! - Atomic memory operations -mod futex; mod membarrier; -pub use self::{futex::*, membarrier::*}; +pub use posix_sync::{sys_futex, sys_get_robust_list, sys_set_robust_list}; + +pub use self::membarrier::*; diff --git a/core/ksyscall/src/sys.rs b/core/ksyscall/src/sys.rs index 6f3a3164..d35b01be 100644 --- a/core/ksyscall/src/sys.rs +++ b/core/ksyscall/src/sys.rs @@ -12,9 +12,9 @@ use core::ffi::c_char; use kbuild_config::ARCH; -use kcore::task::processes; use kerrno::KResult; use kfs::FS_CONTEXT; +use kthread::processes; use linux_raw_sys::{ general::{GRND_INSECURE, GRND_NONBLOCK, GRND_RANDOM}, system::{new_utsname, sysinfo}, diff --git a/core/ksyscall/src/task/clone.rs b/core/ksyscall/src/task/clone.rs index 1c6f8eef..3f605475 100644 --- a/core/ksyscall/src/task/clone.rs +++ b/core/ksyscall/src/task/clone.rs @@ -14,23 +14,20 @@ use alloc::sync::Arc; use bitflags::bitflags; -use kcore::{ - mm::copy_from_kernel, - task::{AsThread, ProcessData, Thread, add_task_to_table}, -}; +use kcore::mm::copy_from_kernel; use kerrno::{KError, KResult}; +use kfd::{FileLike, new_fd_table}; use kfs::FS_CONTEXT; use khal::uspace::UserContext; use kprocess::Pid; -use kservices::task::new_user_task; +use kservices::{file::PidFd, task::new_user_task}; use ksignal::Signo; use kspin::SpinNoIrq; use ktask::{KTaskExt, current, spawn_task}; +use kthread::{ProcessState, ProcessStateConfig, Thread, add_task_to_table}; use linux_raw_sys::general::*; use osvm::VirtMutPtr; -use crate::file::{FD_TABLE, FileLike, PidFd}; - bitflags! { /// Options for use with [`sys_clone`] and [`sys_clone3`]. /// @@ -218,7 +215,8 @@ impl CloneRequest { }; let curr = current(); - let old_proc_data = &curr.as_thread().proc_data; + let current_thread = kthread::current_thread(); + let old_proc_data = current_thread.process_state(); let mut new_task = new_user_task( &curr.name(), @@ -235,7 +233,7 @@ impl CloneRequest { let new_proc_data = if self.flags.contains(CloneFlags::THREAD) { new_task .ctx_mut() - .set_page_table_root(old_proc_data.aspace.lock().page_table_root().into()); + .set_page_table_root(old_proc_data.address_space().lock().page_table_root().into()); old_proc_data.clone() } else { let proc = if self.flags.contains(CloneFlags::PARENT) { @@ -246,9 +244,9 @@ impl CloneRequest { .fork(tid); let aspace = if self.flags.contains(CloneFlags::VM) { - old_proc_data.aspace.clone() + old_proc_data.address_space().clone() } else { - let mut aspace = old_proc_data.aspace.lock(); + let mut aspace = old_proc_data.address_space().lock(); let aspace = aspace.try_clone()?; copy_from_kernel(&mut aspace.lock())?; aspace @@ -262,28 +260,36 @@ impl CloneRequest { } else { Arc::new(SpinNoIrq::new(old_proc_data.signal.actions.lock().clone())) }; - let proc_data = ProcessData::new( + let proc_state = ProcessState::new( proc, - old_proc_data.exe_path.read().clone(), - old_proc_data.cmdline.read().clone(), + old_proc_data.exe_path().read().clone(), + old_proc_data.cmdline().read().clone(), aspace, signal_actions, exit_signal, old_proc_data.credentials.read().clone(), + ProcessStateConfig { + user_heap_base: kcore::config::USER_HEAP_BASE, + user_stack_size: kcore::config::USER_STACK_SIZE, + signal_trampoline: kcore::config::SIGNAL_TRAMPOLINE, + }, ); - proc_data.set_umask(old_proc_data.umask()); + proc_state.set_umask(old_proc_data.umask()); // Inherit heap pointers from parent to ensure child's heap state is consistent after fork - proc_data.set_heap_top(old_proc_data.get_heap_top()); + proc_state.set_heap_top(old_proc_data.heap_top()); { - let mut scope = proc_data.scope.write(); + let mut scope = proc_state.scope().write(); if self.flags.contains(CloneFlags::FILES) { - FD_TABLE.scope_mut(&mut scope).clone_from(&FD_TABLE); + proc_state + .resources + .replace_fd_table(old_proc_data.resources.fd_table()); } else { - FD_TABLE - .scope_mut(&mut scope) + let fd_table = new_fd_table(); + fd_table .write() - .clone_from(&FD_TABLE.read()); + .clone_from(&old_proc_data.resources.fd_table().read()); + proc_state.resources.replace_fd_table(fd_table); } if self.flags.contains(CloneFlags::FS) { @@ -296,7 +302,7 @@ impl CloneRequest { } } - proc_data + proc_state }; new_proc_data.proc.add_thread(tid); diff --git a/core/ksyscall/src/task/ctl.rs b/core/ksyscall/src/task/ctl.rs index d899e142..e44e8e85 100644 --- a/core/ksyscall/src/task/ctl.rs +++ b/core/ksyscall/src/task/ctl.rs @@ -11,10 +11,10 @@ use core::ffi::c_char; -use kcore::task::{AsThread, get_process_data}; use kerrno::{KError, KResult}; use kservices::mm::vm_load_string; use ktask::current; +use kthread::get_process_state; use linux_raw_sys::general::{__user_cap_data_struct, __user_cap_header_struct}; use osvm::{VirtMutPtr, VirtPtr, write_vm_mem}; @@ -28,7 +28,7 @@ fn validate_cap_header(header_ptr: *mut __user_cap_header_struct) -> KResult<()> header_ptr.write_vm(header)?; return Err(KError::InvalidInput); } - let _ = get_process_data(header.pid as u32)?; + let _ = get_process_state(header.pid as u32)?; Ok(()) } @@ -55,12 +55,6 @@ pub fn sys_capset( Ok(0) } -pub fn sys_umask(mask: u32) -> KResult { - let curr = current(); - let old = curr.as_thread().proc_data.replace_umask(mask); - Ok(old as isize) -} - pub fn sys_get_mempolicy( _policy: *mut i32, _nodemask: *mut usize, diff --git a/core/ksyscall/src/task/execve.rs b/core/ksyscall/src/task/execve.rs index eb24e95b..0fee46e3 100644 --- a/core/ksyscall/src/task/execve.rs +++ b/core/ksyscall/src/task/execve.rs @@ -12,7 +12,7 @@ use alloc::{string::ToString, sync::Arc, vec::Vec}; use core::ffi::c_char; -use kcore::{config::USER_HEAP_BASE, mm::load_user_app, task::AsThread}; +use kcore::{config::USER_HEAP_BASE, mm::load_user_app}; use kerrno::{KError, KResult}; use kfs::FS_CONTEXT; use khal::uspace::UserContext; @@ -20,8 +20,6 @@ use kservices::mm::vm_load_string; use ktask::current; use osvm::load_vec_until_null; -use crate::file::FD_TABLE; - pub fn sys_execve( uctx: &mut UserContext, path: *const c_char, @@ -53,15 +51,16 @@ pub fn sys_execve( debug!("sys_execve <= path: {path:?}, args: {args:?}, envs: {envs:?}"); let curr = current(); - let proc_data = &curr.as_thread().proc_data; + let current_thread = kthread::current_thread(); + let proc_state = current_thread.process_state(); - if proc_data.proc.threads().len() > 1 { + if proc_state.proc.threads().len() > 1 { // TODO: dispatch_irq multi-thread case error!("sys_execve: multi-thread not supported"); return Err(KError::WouldBlock); } - let mut aspace = proc_data.aspace.lock(); + let mut aspace = proc_state.address_space().lock(); let (entry_point, user_stack_base) = load_user_app(&mut aspace, Some(path.as_str()), &args, &envs)?; drop(aspace); @@ -69,12 +68,12 @@ pub fn sys_execve( let loc = FS_CONTEXT.lock().resolve(&path)?; curr.set_name(loc.name()); - *proc_data.exe_path.write() = loc.absolute_path()?.to_string(); - *proc_data.cmdline.write() = Arc::new(args); + *proc_state.exe_path().write() = loc.absolute_path()?.to_string(); + *proc_state.cmdline().write() = Arc::new(args); #[cfg(feature = "tee")] { - proc_data.tee_ta_ctx.write().init_ta_ctx( + proc_state.tee_ta_ctx.write().init_ta_ctx( loc.absolute_path()?.to_string().as_str(), tee_task_iface::tasign::get_ta_head_cached(path.as_str())? .unwrap_or_default() @@ -82,16 +81,17 @@ pub fn sys_execve( ); } - proc_data.set_heap_top(USER_HEAP_BASE); - proc_data.credentials.write().apply_exec(); + proc_state.set_heap_top(USER_HEAP_BASE); + proc_state.credentials.write().apply_exec(); - *proc_data.signal.actions.lock() = Default::default(); + *proc_state.signal.actions.lock() = Default::default(); // Clear set_child_tid after exec since the original address is no longer valid - curr.as_thread().set_clear_child_tid(0); + kthread::current_thread().set_clear_child_tid(0); // Close CLOEXEC file descriptors - let mut fd_table = FD_TABLE.write(); + let fd_table = proc_state.resources.fd_table(); + let mut fd_table = fd_table.write(); let cloexec_fds = fd_table .ids() .filter(|it| fd_table.get(*it).unwrap().cloexec) diff --git a/core/ksyscall/src/task/job.rs b/core/ksyscall/src/task/job.rs index 226c392e..a33c018d 100644 --- a/core/ksyscall/src/task/job.rs +++ b/core/ksyscall/src/task/job.rs @@ -2,54 +2,4 @@ // Copyright 2025 KylinSoft Co., Ltd. // See LICENSES for license details. -//! Job control syscalls. -//! -//! This module implements job control operations including: -//! - Session management (getsid, setsid, etc.) -//! - Process group operations (getpgid, setpgid, etc.) -//! - Process group queries - -use kcore::task::{AsThread, get_process_data, get_process_group}; -use kerrno::{KError, KResult}; -use kprocess::Pid; -use ktask::current; - -/// Get the session ID of the process -pub fn sys_getsid(pid: Pid) -> KResult { - Ok(get_process_data(pid)?.proc.group().session().sid() as _) -} - -/// Create a new session and become the session leader -pub fn sys_setsid() -> KResult { - let curr = current(); - let proc = &curr.as_thread().proc_data.proc; - if get_process_group(proc.pid()).is_ok() { - return Err(KError::OperationNotPermitted); - } - - if let Some((session, _)) = proc.create_session() { - Ok(session.sid() as _) - } else { - Ok(proc.pid() as _) - } -} - -/// Get the process group ID of the process -pub fn sys_getpgid(pid: Pid) -> KResult { - Ok(get_process_data(pid)?.proc.group().pgid() as _) -} - -/// Set the process group ID of the process -pub fn sys_setpgid(pid: Pid, pgid: Pid) -> KResult { - let proc = &get_process_data(pid)?.proc; - - if pgid == 0 { - proc.create_group(); - } else if !proc.move_to_group(&get_process_group(pgid)?) { - return Err(KError::OperationNotPermitted); - } - - Ok(0) -} - -// TODO: job control +//! Job-control syscalls now live in `posix-process`. diff --git a/core/ksyscall/src/task/mod.rs b/core/ksyscall/src/task/mod.rs index 29cef034..4bf8127c 100644 --- a/core/ksyscall/src/task/mod.rs +++ b/core/ksyscall/src/task/mod.rs @@ -22,4 +22,7 @@ mod wait; pub use posix_sched::*; -pub use self::{clone::*, clone3::*, ctl::*, execve::*, exit::*, job::*, thread::*, wait::*}; +pub use self::{clone::*, clone3::*, ctl::*, execve::*, exit::*, wait::*}; + +#[cfg(target_arch = "x86_64")] +pub use self::thread::*; diff --git a/core/ksyscall/src/task/thread.rs b/core/ksyscall/src/task/thread.rs index ba7df6b1..537dc8b3 100644 --- a/core/ksyscall/src/task/thread.rs +++ b/core/ksyscall/src/task/thread.rs @@ -2,37 +2,10 @@ // Copyright 2025 KylinSoft Co., Ltd. // See LICENSES for license details. -//! Thread and process identification syscalls. -//! -//! This module implements thread and process identification operations including: -//! - Process ID queries (getpid, getppid, etc.) -//! - Thread ID queries (gettid, etc.) -//! - Architecture-specific controls (arch_prctl, etc.) +//! Architecture-specific thread control syscalls. -use kcore::task::AsThread; +#[cfg(target_arch = "x86_64")] use kerrno::{KError, KResult}; -use ktask::current; - -/// Get the process ID of the current process -pub fn sys_getpid() -> KResult { - Ok(current().as_thread().proc_data.proc.pid() as _) -} - -/// Get the parent process ID of the current process -pub fn sys_getppid() -> KResult { - current() - .as_thread() - .proc_data - .proc - .parent() - .ok_or(KError::NoSuchProcess) - .map(|p| p.pid() as _) -} - -/// Get the thread ID of the current thread -pub fn sys_gettid() -> KResult { - Ok(current().id().as_u64() as _) -} /// ARCH_PRCTL codes /// @@ -57,17 +30,6 @@ enum ArchPrctlCode { SetCpuid = 0x1012, } -/// To set the clear_child_tid field in the task extended data. -/// -/// The set_tid_address() always succeeds -/// Set the thread ID address for thread termination notification -/// Always succeeds and returns the current thread ID -pub fn sys_set_tid_address(clear_child_tid: usize) -> KResult { - let curr = current(); - curr.as_thread().set_clear_child_tid(clear_child_tid); - Ok(curr.id().as_u64() as isize) -} - /// Architecture-specific operations (x86_64 only) /// Supports FS/GS segment base operations and CPUID control #[cfg(target_arch = "x86_64")] diff --git a/core/ksyscall/src/task/wait.rs b/core/ksyscall/src/task/wait.rs index 1c05e5d0..fc215a1e 100644 --- a/core/ksyscall/src/task/wait.rs +++ b/core/ksyscall/src/task/wait.rs @@ -13,11 +13,9 @@ use alloc::vec::Vec; use core::{future::poll_fn, task::Poll}; use bitflags::bitflags; -use kcore::task::AsThread; use kerrno::{KError, KResult, LinuxError}; use kprocess::{Pid, Process}; use ktask::{ - current, future::{block_on, interruptible}, }; use linux_raw_sys::general::{ @@ -74,9 +72,9 @@ pub fn sys_waitpid(pid: i32, exit_code: *mut i32, options: u32) -> KResult KResult KResult Poll::Ready(res), None => { - proc_data.child_exit_event.register(cx.waker()); + proc_state.child_exit_event().register(cx.waker()); Poll::Pending } } diff --git a/core/ksyscall/src/time.rs b/core/ksyscall/src/time.rs index 27cdd964..e7ebc488 100644 --- a/core/ksyscall/src/time.rs +++ b/core/ksyscall/src/time.rs @@ -9,17 +9,15 @@ //! - Time queries (gettimeofday, gettime, etc.) //! - Timer management (setitimer, getitimer, timer_*, etc.) //! - Time conversions and utilities -use kcore::{task::AsThread, time::ITimerType}; use kerrno::{KError, KResult}; use khal::time::{TimeValue, monotonic_time, monotonic_time_nanos, ns2t, wall_time}; -use ktask::current; use linux_raw_sys::general::{ __kernel_clockid_t, CLOCK_BOOTTIME, CLOCK_MONOTONIC, CLOCK_MONOTONIC_COARSE, CLOCK_MONOTONIC_RAW, CLOCK_PROCESS_CPUTIME_ID, CLOCK_REALTIME, CLOCK_REALTIME_COARSE, CLOCK_THREAD_CPUTIME_ID, itimerval, timespec, timeval, }; use osvm::{VirtMutPtr, VirtPtr}; -use posix_types::TimeValueLike; +use posix_types::{ITimerType, TimeValueLike}; /// Get the current time from the specified clock pub fn sys_clock_gettime(clock_id: __kernel_clockid_t, ts: *mut timespec) -> KResult { @@ -29,7 +27,7 @@ pub fn sys_clock_gettime(clock_id: __kernel_clockid_t, ts: *mut timespec) -> KRe monotonic_time() } CLOCK_PROCESS_CPUTIME_ID | CLOCK_THREAD_CPUTIME_ID => { - let (utime, stime) = current().as_thread().time.borrow().output(); + let (utime, stime) = kthread::current_thread().time.borrow().output(); utime + stime } _ => { @@ -73,7 +71,7 @@ pub struct Tms { /// Get timing information including user and system CPU time pub fn sys_times(tms: *mut Tms) -> KResult { - let (utime, stime) = current().as_thread().time.borrow().output(); + let (utime, stime) = kthread::current_thread().time.borrow().output(); let utime = utime.as_micros() as usize; let stime = stime.as_micros() as usize; tms.write_vm(Tms { @@ -88,7 +86,7 @@ pub fn sys_times(tms: *mut Tms) -> KResult { /// Get the current value of a timer pub fn sys_getitimer(which: i32, value: *mut itimerval) -> KResult { let ty = ITimerType::from_repr(which).ok_or(KError::InvalidInput)?; - let (it_interval, it_value) = current().as_thread().time.borrow().get_itimer(ty); + let (it_interval, it_value) = kthread::current_thread().time.borrow().get_itimer(ty); value.write_vm(itimerval { it_interval: timeval::from_time_value(it_interval), @@ -104,7 +102,6 @@ pub fn sys_setitimer( old_value: *mut itimerval, ) -> KResult { let ty = ITimerType::from_repr(which).ok_or(KError::InvalidInput)?; - let curr = current(); let (interval, remained) = match new_value.check_non_null() { Some(new_value) => { @@ -121,8 +118,7 @@ pub fn sys_setitimer( debug!("sys_setitimer <= type: {ty:?}, interval: {interval:?}, remained: {remained:?}"); - let old = curr - .as_thread() + let old = kthread::current_thread() .time .borrow_mut() .set_itimer(ty, interval, remained); diff --git a/entry/Cargo.toml b/entry/Cargo.toml index ba1c96aa..cfdbe0f6 100644 --- a/entry/Cargo.toml +++ b/entry/Cargo.toml @@ -58,6 +58,7 @@ ktask.workspace = true cfg-if.workspace = true linkme.workspace = true kprocess.workspace = true +kthread.workspace = true ksyscall.workspace = true kservices.workspace = true kcore.workspace = true diff --git a/entry/src/entry.rs b/entry/src/entry.rs index 13577d6d..277a12a1 100644 --- a/entry/src/entry.rs +++ b/entry/src/entry.rs @@ -8,17 +8,15 @@ use alloc::{ sync::Arc, }; -use kcore::{ - mm::{copy_from_kernel, load_user_app, new_user_aspace_empty}, - task::{ProcessData, Thread, add_task_to_table}, -}; +use kcore::mm::{copy_from_kernel, load_user_app, new_user_aspace_empty}; use kcred::Credentials; use kfs::FS_CONTEXT; use khal::uspace::UserContext; use kprocess::{Pid, Process}; -use kservices::{file::FD_TABLE, task::new_user_task, vfs::dev::tty::N_TTY}; +use kservices::{task::new_user_task, vfs::dev::tty::N_TTY}; use ksync::Mutex; use ktask::{KTaskExt, spawn_task}; +use kthread::{ProcessState, ProcessStateConfig, Thread, add_task_to_table}; /// Create and run the init process with the given argv/envp. pub fn run_initproc(args: &[String], envs: &[String]) -> i32 { @@ -53,7 +51,7 @@ pub fn run_initproc(args: &[String], envs: &[String]) -> i32 { N_TTY.bind_to(&proc).expect("Failed to bind ntty"); - let proc_data = ProcessData::new( + let proc_state = ProcessState::new( proc, path.to_string(), Arc::new(args.to_vec()), @@ -61,13 +59,17 @@ pub fn run_initproc(args: &[String], envs: &[String]) -> i32 { Arc::default(), None, Credentials::root(), + ProcessStateConfig { + user_heap_base: kcore::config::USER_HEAP_BASE, + user_stack_size: kcore::config::USER_STACK_SIZE, + signal_trampoline: kcore::config::SIGNAL_TRAMPOLINE, + }, ); { - let mut scope = proc_data.scope.write(); - kservices::file::add_stdio(&mut FD_TABLE.scope_mut(&mut scope).write()) + kservices::file::add_stdio(&mut proc_state.resources.fd_table().write()) .expect("Failed to add stdio"); } - let thr = Thread::new(pid, proc_data); + let thr = Thread::new(pid, proc_state); *task.task_ext_mut() = Some(unsafe { KTaskExt::from_impl(thr) }); diff --git a/fs/procfs/Cargo.toml b/fs/procfs/Cargo.toml index 1b9d9131..bbdd371c 100644 --- a/fs/procfs/Cargo.toml +++ b/fs/procfs/Cargo.toml @@ -9,7 +9,7 @@ homepage.workspace = true repository.workspace = true [features] -tee = ["kcore/tee", "dep:tee_task_iface"] +tee = ["kcore/tee", "kthread/tee", "dep:tee_task_iface"] [dependencies] fs-ng-vfs.workspace = true @@ -21,6 +21,7 @@ memaddr.workspace = true memspace.workspace = true memspace_file.workspace = true kprocess.workspace = true +kthread.workspace = true ktask.workspace = true ktracing.workspace = true unittest.workspace = true diff --git a/fs/procfs/src/task.rs b/fs/procfs/src/task.rs index 915f66ca..f0e3a2e5 100644 --- a/fs/procfs/src/task.rs +++ b/fs/procfs/src/task.rs @@ -18,7 +18,6 @@ use fs_ng_vfs::{NodeType, VfsError, VfsResult}; use kcore::vfs::DirMapping; use kcore::{ config::{SIGNAL_TRAMPOLINE, USER_HEAP_BASE, USER_STACK_SIZE, USER_STACK_TOP}, - task::{AsThread, TaskStat, get_process_data, get_task, processes}, vfs::{ NodeOpsMux, RwFile, SeqFileNode, SeqIterator, SimpleDir, SimpleDirOps, SimpleFile, SimpleFileOperation, SimpleFs, @@ -27,6 +26,7 @@ use kcore::{ use khal::paging::MappingFlags; use kprocess::Process; use ktask::{KtaskRef, WeakKtaskRef, current}; +use kthread::{AsThread, TaskStat, get_process_state, get_task, processes}; use memaddr::VirtAddr; use memspace::backend::Backend; use memspace_file::{CowBackend, FileBackend}; @@ -58,7 +58,7 @@ impl SimpleDirOps for ProcessTaskDir { let process = self.process.upgrade().ok_or(VfsError::NotFound)?; let tid = name.parse::().map_err(|_| VfsError::NotFound)?; let task = get_task(tid).map_err(|_| VfsError::NotFound)?; - if task.as_thread().proc_data.proc.pid() != process.pid() { + if task.as_thread().proc_state.proc.pid() != process.pid() { return Err(VfsError::NotFound); } @@ -81,7 +81,7 @@ fn task_status(task: &KtaskRef) -> String { Cpus_allowed_list:\t0\n\ Mems_allowed:\t1\n\ Mems_allowed_list:\t0\n", - task.as_thread().proc_data.proc.pid(), + task.as_thread().proc_state.proc.pid(), task.id().as_u64() ) } @@ -253,9 +253,9 @@ impl SeqIterator for MapsIter { return; }; - let proc_data = &task.as_thread().proc_data; - let heap_top = proc_data.get_heap_top(); - let aspace = proc_data.aspace.lock(); + let proc_state = &task.as_thread().proc_state; + let heap_top = proc_state.heap_top(); + let aspace = proc_state.address_space().lock(); for area in aspace.areas() { let start = area.start().as_usize(); let end = area.end().as_usize(); @@ -457,7 +457,7 @@ impl SimpleDirOps for ThreadDir { fs.clone(), Arc::new(ProcessTaskDir { fs, - process: Arc::downgrade(&task.as_thread().proc_data.proc), + process: Arc::downgrade(&task.as_thread().proc_state.proc), hooks: self.hooks, }), ) @@ -469,7 +469,7 @@ impl SimpleDirOps for ThreadDir { "cgroup" => SimpleFile::new_regular(fs.clone(), move || Ok("0::/\n")).into(), "ns" => SimpleDir::new_maker(fs.clone(), Arc::new(ThreadNsDir { fs })).into(), "cmdline" => SimpleFile::new_regular(fs, move || { - let cmdline = task.as_thread().proc_data.cmdline.read(); + let cmdline = task.as_thread().proc_state.cmdline().read(); let mut buf = Vec::new(); for arg in cmdline.iter() { buf.extend_from_slice(arg.as_bytes()); @@ -507,7 +507,7 @@ impl SimpleDirOps for ThreadDir { ) .into(), "exe" => SimpleFile::new(fs, NodeType::Symlink, move || { - Ok(task.as_thread().proc_data.exe_path.read().clone()) + Ok(task.as_thread().proc_state.exe_path().read().clone()) }) .into(), "fd" => SimpleDir::new_maker( @@ -544,7 +544,7 @@ impl SimpleDirOps for ProcFsHandler { Box::new( processes() .into_iter() - .map(|proc_data| Cow::Owned(proc_data.proc.pid().to_string())) + .map(|proc_state| Cow::Owned(proc_state.proc.pid().to_string())) .chain([Cow::Borrowed("self")]), ) } @@ -554,8 +554,8 @@ impl SimpleDirOps for ProcFsHandler { current().clone() } else { let pid = name.parse::().map_err(|_| VfsError::NotFound)?; - let proc_data = get_process_data(pid).map_err(|_| VfsError::NotFound)?; - proc_data + let proc_state = get_process_state(pid).map_err(|_| VfsError::NotFound)?; + proc_state .proc .threads() .into_iter() diff --git a/fs/procfs/src/tee.rs b/fs/procfs/src/tee.rs index 4af575a5..a6fef5eb 100644 --- a/fs/procfs/src/tee.rs +++ b/fs/procfs/src/tee.rs @@ -7,31 +7,29 @@ use alloc::{borrow::Cow, boxed::Box, sync::Arc, vec::Vec}; use fs_ng_vfs::{VfsError, VfsResult}; -use kcore::{ - task::AsThread, - vfs::{NodeOpsMux, SimpleDir, SimpleDirOps, SimpleFile, SimpleFs}, -}; +use kcore::vfs::{NodeOpsMux, SimpleDir, SimpleDirOps, SimpleFile, SimpleFs}; use ktask::{KtaskRef, WeakKtaskRef}; +use kthread::AsThread; use tee_task_iface::tee_procfs::{ has_ta_info as tee_has_ta_info, render_ta_ctx_uuid as tee_render_ta_ctx_uuid, render_ta_head as tee_render_ta_head, }; pub fn has_ta_info(task: &KtaskRef) -> bool { - let proc_data = &task.as_thread().proc_data; - let ta_ctx = proc_data.tee_ta_ctx.read(); + let proc_state = &task.as_thread().proc_state; + let ta_ctx = proc_state.tee_ta_ctx.read(); tee_has_ta_info(&ta_ctx) } pub fn render_ta_ctx_uuid(task: &KtaskRef) -> Vec { - let proc_data = &task.as_thread().proc_data; - let ta_ctx = proc_data.tee_ta_ctx.read(); + let proc_state = &task.as_thread().proc_state; + let ta_ctx = proc_state.tee_ta_ctx.read(); tee_render_ta_ctx_uuid(&ta_ctx) } pub fn render_ta_head(task: &KtaskRef) -> Vec { - let proc_data = &task.as_thread().proc_data; - let ta_ctx = proc_data.tee_ta_ctx.read(); + let proc_state = &task.as_thread().proc_state; + let ta_ctx = proc_state.tee_ta_ctx.read(); tee_render_ta_head(&ta_ctx) } diff --git a/posix/credentials/src/helpers.rs b/posix/credentials/src/helpers.rs index 7becbc4e..2d9b2937 100644 --- a/posix/credentials/src/helpers.rs +++ b/posix/credentials/src/helpers.rs @@ -20,14 +20,14 @@ pub(crate) fn optional_id(id: u32) -> Option { /// Runs a closure with a read-only snapshot of the current process credentials. pub(crate) fn with_credentials(f: impl FnOnce(&Credentials) -> R) -> R { let curr = current(); - let credentials = curr.as_thread().proc_data.credentials.read(); + let credentials = curr.as_thread().proc_state.credentials.read(); f(&credentials) } /// Runs a closure with mutable access to the current process credentials. pub(crate) fn with_credentials_mut(f: impl FnOnce(&mut Credentials) -> R) -> R { let curr = current(); - let mut credentials = curr.as_thread().proc_data.credentials.write(); + let mut credentials = curr.as_thread().proc_state.credentials.write(); f(&mut credentials) } diff --git a/posix/fs/Cargo.toml b/posix/fs/Cargo.toml index af0923fe..9df6ce38 100644 --- a/posix/fs/Cargo.toml +++ b/posix/fs/Cargo.toml @@ -13,6 +13,7 @@ categories.workspace = true [dependencies] bitflags.workspace = true +kfd.workspace = true kcore.workspace = true kerrno.workspace = true kfs.workspace = true @@ -21,7 +22,7 @@ kio.workspace = true klogger.workspace = true kpoll.workspace = true kservices.workspace = true -ktask.workspace = true +kthread.workspace = true fs-ng-vfs.workspace = true linux-raw-sys = { workspace = true, features = ["ioctl", "loop_device"] } linux_sysno.workspace = true diff --git a/posix/fs/src/ctl.rs b/posix/fs/src/ctl.rs index 01597466..f06cebe4 100644 --- a/posix/fs/src/ctl.rs +++ b/posix/fs/src/ctl.rs @@ -20,12 +20,11 @@ use core::{ }; use fs_ng_vfs::{MetadataUpdate, NodePermission, NodeType, path::Path}; -use kcore::task::AsThread; use kerrno::{KError, KResult}; +use kfd::get_file_like; use kfs::{FS_CONTEXT, FsContext}; use khal::time::wall_time; -use kservices::file::{Directory, FileLike, get_file_like}; -use ktask::current; +use kservices::file::Directory; use linux_raw_sys::{ general::*, ioctl::{FIONBIO, TIOCGWINSZ}, @@ -34,7 +33,6 @@ use osvm::VirtPtr; use posix_types::{TimeValueLike, UserConstPtr, UserPtr}; use crate::path::{resolve_at, with_fs}; - /// The ioctl() system call manipulates the underlying device parameters /// of special files. pub fn sys_ioctl(fd: i32, cmd: u32, arg: usize) -> KResult { @@ -106,7 +104,7 @@ pub fn sys_mkdirat(dirfd: i32, path: UserConstPtr, mode: u32) -> KResult let path = path.load_string()?; debug!("sys_mkdirat <= dirfd: {dirfd}, path: {path}, mode: {mode}"); - let mode = mode & !current().as_thread().proc_data.umask(); + let mode = mode & !kthread::current_thread().process_state().umask(); let mode = NodePermission::from_bits_truncate(mode as u16); with_fs(dirfd, |fs| match fs.create_dir(&path, mode) { diff --git a/posix/fs/src/fd_ops.rs b/posix/fs/src/fd_ops.rs index cf4793b5..094cd88e 100644 --- a/posix/fs/src/fd_ops.rs +++ b/posix/fs/src/fd_ops.rs @@ -11,17 +11,15 @@ use core::{ ffi::c_int, - mem, - ops::{Deref, DerefMut}, }; use bitflags::bitflags; -use kcore::task::AsThread; use kerrno::{KError, KResult}; -use kservices::file::{FD_TABLE, FileLike, Pipe, add_file_like, close_file_like, get_file_like}; -use ktask::current; +use kfd::{ + FileLike, add_file_like, close_file_like, current_fd_table, get_file_like, new_fd_table, +}; +use kservices::file::Pipe; use linux_raw_sys::general::*; -use osvm::{VirtMutPtr, VirtPtr}; use posix_types::UserPtr; /// Closes the specified file descriptor. @@ -47,16 +45,17 @@ pub fn sys_close_range(first: i32, last: i32, flags: u32) -> KResult { let flags = CloseRangeFlags::from_bits(flags).ok_or(KError::InvalidInput)?; debug!("sys_close_range <= fds: [{first}, {last}], flags: {flags:?}"); if flags.contains(CloseRangeFlags::UNSHARE) { - // TODO: optimize - let curr = current(); - let mut scope = curr.as_thread().proc_data.scope.write(); - let mut guard = FD_TABLE.scope_mut(&mut scope); - let old_files = mem::take(guard.deref_mut()); - old_files.write().clone_from(old_files.read().deref()); + let current_thread = kthread::current_thread(); + let proc_state = current_thread.process_state(); + let old_files = proc_state.resources.fd_table(); + let new_files = new_fd_table(); + new_files.write().clone_from(&old_files.read()); + proc_state.resources.replace_fd_table(new_files); } let cloexec = flags.contains(CloseRangeFlags::CLOEXEC); - let mut fd_table = FD_TABLE.write(); + let fd_table = current_fd_table(); + let mut fd_table = fd_table.write(); if let Some(max_index) = fd_table.ids().next_back() { for fd in first..=last.min(max_index as i32) { if cloexec { @@ -111,7 +110,8 @@ pub fn sys_dup3(old_fd: c_int, new_fd: c_int, flags: c_int) -> KResult { return Err(KError::InvalidInput); } - let mut fd_table = FD_TABLE.write(); + let fd_table = current_fd_table(); + let mut fd_table = fd_table.write(); let mut f = fd_table .get(old_fd as _) .cloned() @@ -157,7 +157,7 @@ pub fn sys_fcntl(fd: c_int, cmd: c_int, arg: usize) -> KResult { Ok(ret as _) } F_GETFD => { - let cloexec = FD_TABLE + let cloexec = current_fd_table() .read() .get(fd as _) .ok_or(KError::BadFileDescriptor)? @@ -166,7 +166,7 @@ pub fn sys_fcntl(fd: c_int, cmd: c_int, arg: usize) -> KResult { } F_SETFD => { let cloexec = arg & FD_CLOEXEC as usize != 0; - FD_TABLE + current_fd_table() .write() .get_mut(fd as _) .ok_or(KError::BadFileDescriptor)? diff --git a/posix/fs/src/io.rs b/posix/fs/src/io.rs index 4342be57..3da88689 100644 --- a/posix/fs/src/io.rs +++ b/posix/fs/src/io.rs @@ -18,18 +18,18 @@ use core::{ }; use kerrno::{KError, KResult, LinuxError}; +use kfd::{FileLike, get_file_like}; use kfs::{FS_CONTEXT, FileFlags, OpenOptions}; use kio::{Seek, SeekFrom}; use kpoll::{IoEvents, Pollable}; use kservices::{ - file::{Directory, File, FileLike, Pipe, get_file_like}, + file::{Directory, File, Pipe}, io::{IoVec, IoVectorBuf}, mm::{VmBytes, VmBytesMut}, }; use ktask::current; use linux_raw_sys::general::__kernel_off_t; use linux_sysno::Sysno; -use osvm::{VirtMutPtr, VirtPtr}; use posix_types::{UserConstPtr, UserPtr}; struct DummyFd; @@ -322,11 +322,11 @@ pub fn sys_fallocate( pub fn sys_fsync(fd: c_int) -> KResult { debug!("sys_fsync <= {fd}"); // Synchronize file to disk - syncs both data and metadata - let any_file = kservices::file::get_file_like(fd)?; + let any_file = get_file_like(fd)?; if let Ok(f) = any_file.clone().downcast_arc::() { f.inner().sync(false)?; return Ok(0); - } else if let Ok(d) = any_file.downcast_arc::() { + } else if let Ok(d) = any_file.downcast_arc::() { d.inner().sync(false)?; return Ok(0); } @@ -337,11 +337,11 @@ pub fn sys_fsync(fd: c_int) -> KResult { pub fn sys_fdatasync(fd: c_int) -> KResult { debug!("sys_fdatasync <= {fd}"); // Synchronize file data to disk - only syncs data, not metadata - let any_file = kservices::file::get_file_like(fd)?; + let any_file = get_file_like(fd)?; if let Ok(f) = any_file.clone().downcast_arc::() { f.inner().sync(true)?; return Ok(0); - } else if let Ok(d) = any_file.downcast_arc::() { + } else if let Ok(d) = any_file.downcast_arc::() { d.inner().sync(true)?; return Ok(0); } diff --git a/posix/fs/src/open.rs b/posix/fs/src/open.rs index 266e0680..a8a3738e 100644 --- a/posix/fs/src/open.rs +++ b/posix/fs/src/open.rs @@ -8,14 +8,13 @@ use alloc::{format, string::ToString, sync::Arc}; use core::ffi::{c_char, c_int}; use fs_ng_vfs::{DirEntry, FileNode, Location, NodeType, Reference}; -use kcore::{task::AsThread, vfs::Device}; +use kcore::vfs::Device; use kerrno::{KError, KResult}; use kfs::{FS_CONTEXT, FileBackend, OpenOptions, OpenResult}; use kservices::{ file::{Directory, File, FileLike, add_file_like}, vfs::dev::tty, }; -use ktask::current; use linux_raw_sys::general::*; use posix_types::UserConstPtr; @@ -80,9 +79,8 @@ fn add_to_fd(result: OpenResult, flags: u32) -> KResult { let loc = Location::new(file.location().mountpoint().clone(), entry); file = kfs::File::new(FileBackend::Direct(loc), file.flags()); } else if inner.is::() { - let term = current() - .as_thread() - .proc_data + let term = kthread::current_thread() + .process_state() .proc .group() .session() @@ -120,7 +118,7 @@ pub fn sys_openat( debug!("sys_openat <= {dirfd} {path:?} {flags:#o} {mode:#o}"); let raw_flags = flags as u32; - let mode = mode & !current().as_thread().proc_data.umask(); + let mode = mode & !kthread::current_thread().process_state().umask(); let options = flags_to_options(flags, mode, current_effective_ids()); if let PathSource::Resolved(path) = resolve_open_path_source(dirfd, Some(&path), raw_flags)? diff --git a/posix/fs/src/path.rs b/posix/fs/src/path.rs index 89204874..9c781a4b 100644 --- a/posix/fs/src/path.rs +++ b/posix/fs/src/path.rs @@ -8,11 +8,11 @@ use alloc::{borrow::ToOwned, string::String, sync::Arc}; use core::ffi::c_int; use fs_ng_vfs::{Location, Metadata}; -use kcore::task::{AsThread, get_process_data}; use kerrno::{KError, KResult}; +use kfd::{FileLike, Kstat, get_file_like}; use kfs::{FS_CONTEXT, FsContext}; -use kservices::file::{Directory, FD_TABLE, File, FileLike, Kstat, get_file_like}; -use ktask::current; +use kservices::file::{Directory, File}; +use kthread::{current_thread, get_process_state}; use linux_raw_sys::general::{AT_EMPTY_PATH, AT_FDCWD, AT_SYMLINK_NOFOLLOW, O_NOFOLLOW, O_PATH}; /// The coarse shape of a path string before any runtime resolution. @@ -232,19 +232,15 @@ fn resolve_empty_path(dirfd: c_int, flags: u32) -> KResult { ResolvedPath::from_file_like(file_like.path().into_owned(), file_like).map(PathSource::Resolved) } -fn current_pid() -> u32 { - current().as_thread().proc_data.proc.pid() -} - fn is_live_procfd_path(path: &str) -> bool { matches!( - classify_path(path, current_pid()), + classify_path(path, current_thread().pid()), ClassifiedPath::ProcFd(_) ) } fn resolve_live_procfd(path: &str) -> KResult> { - let current_pid = current_pid(); + let current_pid = current_thread().pid(); let procfd = match classify_path(path, current_pid) { ClassifiedPath::Plain(_) => return Ok(None), ClassifiedPath::InvalidProcFd => return Err(KError::NotFound), @@ -314,10 +310,10 @@ pub fn resolve_at(dirfd: c_int, path: Option<&str>, flags: u32) -> KResult KResult> { - let proc_data = get_process_data(pid)?; - let scope = proc_data.scope.read(); - FD_TABLE - .scope(&scope) + let proc_state = get_process_state(pid)?; + proc_state + .resources + .fd_table() .read() .get(fd as usize) .map(|entry| entry.inner.clone()) diff --git a/posix/fs/src/pipe.rs b/posix/fs/src/pipe.rs index d33255f0..6074cc2b 100644 --- a/posix/fs/src/pipe.rs +++ b/posix/fs/src/pipe.rs @@ -12,7 +12,8 @@ use core::ffi::c_int; use bitflags::bitflags; use kerrno::KResult; -use kservices::file::{FileLike, Pipe, close_file_like}; +use kfd::{FileLike, close_file_like}; +use kservices::file::Pipe; use linux_raw_sys::general::{O_CLOEXEC, O_NONBLOCK}; use osvm::VirtMutPtr; use posix_types::UserPtr; diff --git a/posix/fs/src/stat.rs b/posix/fs/src/stat.rs index 8d62f816..f5d07907 100644 --- a/posix/fs/src/stat.rs +++ b/posix/fs/src/stat.rs @@ -8,8 +8,9 @@ use core::ffi::{c_char, c_int}; use fs_ng_vfs::{Location, NodePermission}; use kerrno::{KError, KResult}; +use kfd::FileLike; use kfs::FS_CONTEXT; -use kservices::file::{File, FileLike}; +use kservices::file::File; use linux_raw_sys::general::{ __kernel_fsid_t, AT_EMPTY_PATH, R_OK, W_OK, X_OK, stat, statfs, statx, }; diff --git a/posix/ipc/Cargo.toml b/posix/ipc/Cargo.toml index 5c8f55f6..a5ebfed3 100644 --- a/posix/ipc/Cargo.toml +++ b/posix/ipc/Cargo.toml @@ -10,13 +10,12 @@ repository.workspace = true documentation.workspace = true [dependencies] -kcore = { workspace = true } kerrno = { workspace = true } khal = { workspace = true } klogger = { workspace = true } kprocess = { workspace = true } +kthread = { workspace = true } ksync = { workspace = true } -ktask = { workspace = true } linux-raw-sys = { workspace = true } memaddr = { workspace = true } memspace = { workspace = true } diff --git a/posix/ipc/src/msg.rs b/posix/ipc/src/msg.rs index 93aa8bb7..b5cf03ce 100644 --- a/posix/ipc/src/msg.rs +++ b/posix/ipc/src/msg.rs @@ -8,12 +8,11 @@ use alloc::{collections::BTreeMap, sync::Arc, vec::Vec}; use core::mem::size_of; use bytemuck::AnyBitPattern; -use kcore::task::AsThread; use kerrno::{KError, KResult, LinuxError}; use khal::time::monotonic_time_nanos; use kprocess::Pid; use ksync::Mutex; -use ktask::current; +use kthread::current_process_state; use linux_raw_sys::general::*; use osvm::{VirtMutPtr, VirtPtr}; use posix_types::{IpcPerm, UserConstPtr, UserPtr}; @@ -254,12 +253,10 @@ pub struct UserMsgbuf { } pub fn sys_msgget(key: i32, msgflg: i32) -> KResult { - let current = current(); - let thread = current.as_thread(); - let proc_data = &thread.proc_data; + let proc_state = current_process_state(); let current_uid: u32 = 0; let current_gid: u32 = 0; - let current_pid = proc_data.proc.pid(); + let current_pid = proc_state.proc.pid(); let mut msg_manager = MSG_MANAGER.lock(); @@ -336,12 +333,10 @@ pub fn sys_msgsnd( if msgsz > MSGMAX { return Err(KError::from(LinuxError::EINVAL)); } - let current = current(); - let thread = current.as_thread(); - let proc_data = &thread.proc_data; + let proc_state = current_process_state(); let current_uid: u32 = 0; let current_gid: u32 = 0; - let current_pid = proc_data.proc.pid(); + let current_pid = proc_state.proc.pid(); let flags = MsgSndFlags::from_bits_truncate(msgflg); let msg_queue = { @@ -405,12 +400,10 @@ pub fn sys_msgrcv( msgflg: i32, ) -> KResult { let flags = MsgRcvFlags::from_bits_truncate(msgflg); - let current = current(); - let thread = current.as_thread(); - let proc_data = &thread.proc_data; + let proc_state = current_process_state(); let current_uid: u32 = 0; let current_gid: u32 = 0; - let current_pid = proc_data.proc.pid(); + let current_pid = proc_state.proc.pid(); if flags.contains(MsgRcvFlags::MSG_COPY) { if !flags.contains(MsgRcvFlags::IPC_NOWAIT) { diff --git a/posix/ipc/src/shm.rs b/posix/ipc/src/shm.rs index ca0efc5d..3ebdbe08 100644 --- a/posix/ipc/src/shm.rs +++ b/posix/ipc/src/shm.rs @@ -6,7 +6,6 @@ use alloc::{collections::btree_map::BTreeMap, sync::Arc, vec::Vec}; -use kcore::task::AsThread; use kerrno::{KError, KResult}; use khal::{ paging::{MappingFlags, PageSize}, @@ -14,7 +13,7 @@ use khal::{ }; use kprocess::Pid; use ksync::Mutex; -use ktask::current; +use kthread::current_process_state; use linux_raw_sys::{ctypes::c_ushort, general::*}; use memaddr::{PAGE_SIZE_4K, VirtAddr, VirtAddrRange}; use memspace::backend::{Backend, SharedPages}; @@ -330,7 +329,7 @@ pub fn sys_shmget(key: i32, size: usize, shmflg: usize) -> KResult { mapping_flags.insert(MappingFlags::EXECUTE); } - let cur_pid = current().as_thread().proc_data.proc.pid(); + let cur_pid = kthread::current_thread().pid(); let mut shm_manager = SHM_MANAGER.lock(); if key != IPC_PRIVATE @@ -372,10 +371,9 @@ pub fn sys_shmat(shmid: i32, addr: usize, shmflg: u32) -> KResult { mapping_flags.remove(MappingFlags::WRITE); } - let curr = current(); - let proc_data = &curr.as_thread().proc_data; - let pid = proc_data.proc.pid(); - let mut aspace = proc_data.aspace.lock(); + let proc_state = current_process_state(); + let pid = proc_state.proc.pid(); + let mut aspace = proc_state.address_space().lock(); let start_aligned = memaddr::align_down_4k(addr); let length = shm_inner.page_num * PAGE_SIZE_4K; @@ -454,10 +452,9 @@ pub fn sys_shmctl(shmid: i32, cmd: u32, buf: UserPtr) -> KResult pub fn sys_shmdt(shmaddr: usize) -> KResult { let shmaddr = VirtAddr::from(shmaddr); - let curr = current(); - let proc_data = &curr.as_thread().proc_data; + let proc_state = current_process_state(); - let pid = proc_data.proc.pid(); + let pid = proc_state.proc.pid(); let shmid = { let shm_manager = SHM_MANAGER.lock(); shm_manager @@ -474,7 +471,7 @@ pub fn sys_shmdt(shmaddr: usize) -> KResult { let mut shm_inner = shm_inner.lock(); let va_range = shm_inner.get_addr_range(pid).ok_or(KError::InvalidInput)?; - let mut aspace = proc_data.aspace.lock(); + let mut aspace = proc_state.address_space().lock(); aspace.unmap(va_range.start, va_range.size())?; let mut shm_manager = SHM_MANAGER.lock(); diff --git a/posix/mm/Cargo.toml b/posix/mm/Cargo.toml index ff10556a..75bd6c40 100644 --- a/posix/mm/Cargo.toml +++ b/posix/mm/Cargo.toml @@ -15,8 +15,8 @@ kerrno = { workspace = true } kfs = { workspace = true } khal = { workspace = true } klogger = { workspace = true } +kthread = { workspace = true } kservices = { workspace = true } -ktask = { workspace = true } linux-raw-sys = { workspace = true } memaddr = { workspace = true } memspace = { workspace = true } diff --git a/posix/mm/src/brk.rs b/posix/mm/src/brk.rs index 1854529e..8e9dac39 100644 --- a/posix/mm/src/brk.rs +++ b/posix/mm/src/brk.rs @@ -4,20 +4,16 @@ //! Heap management syscalls. -use kcore::{ - config::{USER_HEAP_BASE, USER_HEAP_SIZE, USER_HEAP_SIZE_MAX}, - task::AsThread, -}; +use kcore::config::{USER_HEAP_BASE, USER_HEAP_SIZE, USER_HEAP_SIZE_MAX}; use kerrno::KResult; use khal::paging::{MappingFlags, PageSize}; -use ktask::current; +use kthread::current_process_state; use memaddr::{VirtAddr, align_up_4k}; use memspace_file::new_alloc; pub fn sys_brk(addr: usize) -> KResult { - let curr = current(); - let proc_data = &curr.as_thread().proc_data; - let current_top = proc_data.get_heap_top() as usize; + let proc_state = current_process_state(); + let current_top = proc_state.heap_top(); let heap_limit = USER_HEAP_BASE + USER_HEAP_SIZE_MAX; if addr == 0 { @@ -40,8 +36,8 @@ pub fn sys_brk(addr: usize) -> KResult { let expand_size = new_top_aligned.saturating_sub(expand_start.as_usize()); if expand_size > 0 - && proc_data - .aspace + && proc_state + .address_space() .lock() .map( expand_start, @@ -60,8 +56,8 @@ pub fn sys_brk(addr: usize) -> KResult { let shrink_size = current_top_aligned.saturating_sub(shrink_start.as_usize()); if shrink_size > 0 - && proc_data - .aspace + && proc_state + .address_space() .lock() .unmap(shrink_start, shrink_size) .is_err() @@ -70,6 +66,6 @@ pub fn sys_brk(addr: usize) -> KResult { } } - proc_data.set_heap_top(addr); + proc_state.set_heap_top(addr); Ok(addr as isize) } diff --git a/posix/mm/src/mincore.rs b/posix/mm/src/mincore.rs index f0427b8a..9ac094f7 100644 --- a/posix/mm/src/mincore.rs +++ b/posix/mm/src/mincore.rs @@ -6,10 +6,9 @@ use alloc::vec; -use kcore::task::AsThread; use kerrno::{KError, KResult}; use khal::paging::MappingFlags; -use ktask::current; +use kthread::current_process_state; use memaddr::{MemoryAddr, PAGE_SIZE_4K, VirtAddr}; use posix_types::UserPtr; @@ -67,8 +66,8 @@ pub fn sys_mincore(addr: usize, length: usize, vec: UserPtr) -> KResult { // TODO(mivik): file mmap page size - new_file( - start, - cache, - file.flags(), - offset, - &curr.as_thread().proc_data.aspace, - ) + new_file(start, cache, file.flags(), offset, proc_state.address_space()) } FileBackend::Direct(loc) => { if let Ok(device) = loc.entry().downcast::() { @@ -219,13 +210,9 @@ pub fn sys_mmap( start.as_usize() as isize - range.start.as_usize() as isize, ) } - DeviceMmap::Cache(cache) => new_file( - start, - cache, - file.flags(), - offset, - &curr.as_thread().proc_data.aspace, - ), + DeviceMmap::Cache(cache) => { + new_file(start, cache, file.flags(), offset, proc_state.address_space()) + } } } else { // Preserve MAP_SHARED semantics for direct-opened regular files. @@ -234,7 +221,7 @@ pub fn sys_mmap( CachedFile::get_or_create(loc.clone()), file.flags(), offset, - &curr.as_thread().proc_data.aspace, + proc_state.address_space(), ) } } @@ -263,8 +250,8 @@ pub fn sys_mmap( pub fn sys_munmap(addr: usize, length: usize) -> KResult { debug!("sys_munmap <= addr: {addr:#x}, length: {length:x}"); - let curr = current(); - let mut aspace = curr.as_thread().proc_data.aspace.lock(); + let proc_state = current_process_state(); + let mut aspace = proc_state.address_space().lock(); let length = align_up_4k(length); let start_addr = VirtAddr::from(addr); aspace.unmap(start_addr, length)?; @@ -282,8 +269,8 @@ pub fn sys_mprotect(addr: usize, length: usize, prot: u32) -> KResult { return Err(KError::InvalidInput); } - let curr = current(); - let mut aspace = curr.as_thread().proc_data.aspace.lock(); + let proc_state = current_process_state(); + let mut aspace = proc_state.address_space().lock(); let length = align_up_4k(length); let start_addr = VirtAddr::from(addr); aspace.protect(start_addr, length, permission_flags.into())?; @@ -304,8 +291,8 @@ pub fn sys_mremap(addr: usize, old_size: usize, new_size: usize, flags: u32) -> } let addr = VirtAddr::from(addr); - let curr = current(); - let aspace = curr.as_thread().proc_data.aspace.lock(); + let proc_state = current_process_state(); + let aspace = proc_state.address_space().lock(); let old_size = align_up_4k(old_size); let new_size = align_up_4k(new_size); diff --git a/posix/process/Cargo.toml b/posix/process/Cargo.toml new file mode 100644 index 00000000..d8fe6a84 --- /dev/null +++ b/posix/process/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "posix-process" +description = "POSIX process syscall implementations" +version.workspace = true +edition.workspace = true +authors.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +documentation.workspace = true + +[dependencies] +kerrno.workspace = true +khal.workspace = true +kprocess.workspace = true +ktask.workspace = true +kthread.workspace = true +linux-raw-sys.workspace = true +osvm.workspace = true +posix-types.workspace = true diff --git a/posix/process/src/lib.rs b/posix/process/src/lib.rs new file mode 100644 index 00000000..2da9acb0 --- /dev/null +++ b/posix/process/src/lib.rs @@ -0,0 +1,171 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2025 KylinSoft Co., Ltd. +// See LICENSES for license details. + +//! POSIX process and thread syscall implementations. +//! +//! - Process and thread IDs (`getpid`, `getppid`, `gettid`) +//! - Thread-local process state hooks (`set_tid_address`, `umask`) +//! - Job control (`getsid`, `setsid`, `getpgid`, `setpgid`) +//! - Resource usage accounting (`getrusage`) + +#![no_std] + +macro_rules! __log_prefix { + () => { + "process: " + }; +} + +use khal::time::TimeValue; +use kerrno::{KError, KResult}; +use kprocess::{Pid, Process}; +use ktask::current; +use kthread::{AsThread, Thread, get_process_group, get_process_state, get_task}; +use linux_raw_sys::general::{__kernel_old_timeval, rusage}; +use osvm::VirtMutPtr; +use posix_types::TimeValueLike; + +/// Returns the process ID of the current process. +pub fn sys_getpid() -> KResult { + Ok(kthread::current_thread().pid() as _) +} + +/// Returns the parent process ID of the current process. +pub fn sys_getppid() -> KResult { + let current_thread = kthread::current_thread(); + current_thread + .process_state() + .proc + .parent() + .ok_or(KError::NoSuchProcess) + .map(|parent| parent.pid() as _) +} + +/// Returns the thread ID of the current thread. +pub fn sys_gettid() -> KResult { + Ok(current().id().as_u64() as _) +} + +/// Sets the `clear_child_tid` pointer for the current thread. +pub fn sys_set_tid_address(clear_child_tid: usize) -> KResult { + let current = current(); + kthread::current_thread().set_clear_child_tid(clear_child_tid); + Ok(current.id().as_u64() as isize) +} + +/// Returns the session ID of the given process. +pub fn sys_getsid(pid: Pid) -> KResult { + Ok(get_process_state(pid)?.proc.group().session().sid() as _) +} + +/// Creates a new session and makes the caller its leader. +pub fn sys_setsid() -> KResult { + let current_thread = kthread::current_thread(); + let proc = ¤t_thread.process_state().proc; + if get_process_group(proc.pid()).is_ok() { + return Err(KError::OperationNotPermitted); + } + + if let Some((session, _)) = proc.create_session() { + Ok(session.sid() as _) + } else { + Ok(proc.pid() as _) + } +} + +/// Returns the process group ID of the given process. +pub fn sys_getpgid(pid: Pid) -> KResult { + Ok(get_process_state(pid)?.proc.group().pgid() as _) +} + +/// Sets the process group ID of the given process. +pub fn sys_setpgid(pid: Pid, pgid: Pid) -> KResult { + let proc = &get_process_state(pid)?.proc; + + if pgid == 0 { + proc.create_group(); + } else if !proc.move_to_group(&get_process_group(pgid)?) { + return Err(KError::OperationNotPermitted); + } + + Ok(0) +} + +/// Sets the process umask and returns the previous value. +pub fn sys_umask(mask: u32) -> KResult { + let old = kthread::current_thread().process_state().replace_umask(mask); + Ok(old as isize) +} + +#[derive(Default)] +struct Rusage { + utime: TimeValue, + stime: TimeValue, +} + +impl Rusage { + fn from_thread(thread: &Thread) -> Self { + let (utime, stime) = thread.time.borrow().output(); + Self { utime, stime } + } + + fn collate(mut self, other: Rusage) -> Self { + self.utime += other.utime; + self.stime += other.stime; + self + } +} + +impl From for rusage { + fn from(value: Rusage) -> Self { + // SAFETY: `rusage` is a POD struct from `linux_raw_sys`. All-zeroes is a valid + // initial state for every field (integer counters become 0, timevals become 0). + let mut usage: rusage = unsafe { core::mem::zeroed() }; + usage.ru_utime = __kernel_old_timeval::from_time_value(value.utime); + usage.ru_stime = __kernel_old_timeval::from_time_value(value.stime); + usage + } +} + +fn self_rusage(proc: &Process) -> Rusage { + proc.threads().into_iter().fold(Rusage::default(), |acc, tid| { + if let Ok(task) = get_task(tid) { + acc.collate(Rusage::from_thread(task.as_thread())) + } else { + acc + } + }) +} + +fn children_rusage(proc: &Process) -> Rusage { + let current = current(); + proc.threads().into_iter().fold(Rusage::default(), |acc, child| { + if let Ok(task) = get_task(child) + && !current.ptr_eq(&task) + { + acc.collate(Rusage::from_thread(task.as_thread())) + } else { + acc + } + }) +} + +/// Returns resource usage information for the current process, its children, or the current thread. +pub fn sys_getrusage(who: i32, usage: *mut rusage) -> KResult { + const RUSAGE_SELF: i32 = linux_raw_sys::general::RUSAGE_SELF as i32; + const RUSAGE_CHILDREN: i32 = linux_raw_sys::general::RUSAGE_CHILDREN; + const RUSAGE_THREAD: i32 = linux_raw_sys::general::RUSAGE_THREAD as i32; + + let current_thread = kthread::current_thread(); + let proc = ¤t_thread.process_state().proc; + let result = match who { + RUSAGE_SELF => self_rusage(proc), + RUSAGE_CHILDREN => children_rusage(proc), + RUSAGE_THREAD => Rusage::from_thread(¤t_thread), + _ => return Err(KError::InvalidInput), + }; + + usage.write_vm(result.into())?; + Ok(0) +} diff --git a/posix/sched/Cargo.toml b/posix/sched/Cargo.toml index 7bff05e0..57b79df6 100644 --- a/posix/sched/Cargo.toml +++ b/posix/sched/Cargo.toml @@ -11,10 +11,10 @@ documentation.workspace = true [dependencies] kbuild_config = { workspace = true } -kcore = { workspace = true } kerrno = { workspace = true } khal = { workspace = true } klogger = { workspace = true } +kthread = { workspace = true } ktask = { workspace = true } linux-raw-sys = { workspace = true } osvm = { workspace = true } diff --git a/posix/sched/src/lib.rs b/posix/sched/src/lib.rs index 54d6765e..96051e8c 100644 --- a/posix/sched/src/lib.rs +++ b/posix/sched/src/lib.rs @@ -15,13 +15,13 @@ #[macro_use] extern crate klogger; -use kcore::task::{get_process_data, get_process_group}; use kerrno::{KError, KResult}; use khal::time::TimeValue; use ktask::{ KCpuMask, current, future::{block_on, interruptible, sleep}, }; +use kthread::{get_process_state, get_process_group}; use linux_raw_sys::general::{ __kernel_clockid_t, CLOCK_MONOTONIC, CLOCK_REALTIME, PRIO_PGRP, PRIO_PROCESS, PRIO_USER, SCHED_RR, TIMER_ABSTIME, timespec, @@ -164,7 +164,7 @@ pub fn sys_getpriority(which: u32, who: u32) -> KResult { match which { PRIO_PROCESS => { if who != 0 { - let _proc = get_process_data(who)?; + let _proc = get_process_state(who)?; } Ok(20) } @@ -195,7 +195,7 @@ pub fn sys_setpriority(which: u32, who: u32, prio: i32) -> KResult { match which { PRIO_PROCESS => { if who != 0 { - let _proc = get_process_data(who)?; + let _proc = get_process_state(who)?; } Ok(0) } diff --git a/posix/signal/Cargo.toml b/posix/signal/Cargo.toml new file mode 100644 index 00000000..0bfdec36 --- /dev/null +++ b/posix/signal/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "posix-signal" +description = "POSIX signal syscall implementations" +version.workspace = true +edition.workspace = true +authors.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +documentation.workspace = true + +[dependencies] +kerrno.workspace = true +khal.workspace = true +klogger.workspace = true +kprocess.workspace = true +kservices.workspace = true +ksignal.workspace = true +ktask.workspace = true +kthread.workspace = true +linux-raw-sys.workspace = true +osvm.workspace = true +posix-types.workspace = true diff --git a/core/ksyscall/src/signal.rs b/posix/signal/src/lib.rs similarity index 56% rename from core/ksyscall/src/signal.rs rename to posix/signal/src/lib.rs index 9c8d7f21..f2172c49 100644 --- a/core/ksyscall/src/signal.rs +++ b/posix/signal/src/lib.rs @@ -2,20 +2,26 @@ // Copyright 2025 KylinSoft Co., Ltd. // See LICENSES for license details. -//! Signal handling syscalls. +//! POSIX signal syscall implementations. //! -//! This module implements signal-related system calls including: -//! - Signal mask manipulation (rt_sigprocmask, rt_sigaction, etc.) -//! - Signal sending (kill, tgkill, sigqueue, etc.) -//! - Signal waiting (pause, rt_sigsuspend, etc.) -//! - Alternate signal stacks (sigaltstack) -//! - Real-time signal operations +//! - Signal mask manipulation (`rt_sigprocmask`, `rt_sigaction`, `rt_sigpending`) +//! - Signal delivery (`kill`, `tkill`, `tgkill`, realtime queueing) +//! - Signal wait/return flow (`rt_sigreturn`, `rt_sigtimedwait`, `rt_sigsuspend`) +//! - Alternate signal stack (`sigaltstack`) + +#![no_std] + +#[macro_use] +extern crate klogger; + +macro_rules! __log_prefix { + () => { + "signal: " + }; +} + use core::{future::poll_fn, task::Poll}; -use kcore::task::{ - AsThread, processes, send_signal_to_process, send_signal_to_process_group, - send_signal_to_thread, -}; use kerrno::{KError, KResult, LinuxError}; use khal::uspace::UserContext; use kprocess::Pid; @@ -25,6 +31,9 @@ use ktask::{ current, future::{self, block_on}, }; +use kthread::{ + processes, send_signal_to_process, send_signal_to_process_group, send_signal_to_thread, +}; use linux_raw_sys::general::{ MINSIGSTKSZ, SI_TKILL, SI_USER, SIG_BLOCK, SIG_SETMASK, SIG_UNBLOCK, kernel_sigaction, siginfo, timespec, @@ -33,22 +42,21 @@ use osvm::{VirtMutPtr, VirtPtr}; use posix_types::TimeValueLike; /// Validates that the signal set size matches the expected size. -pub(crate) fn check_sigset_size(size: usize) -> KResult<()> { +pub fn check_sigset_size(size: usize) -> KResult<()> { if size != size_of::() && size != 0 { return Err(KError::InvalidInput); } Ok(()) } -/// Converts a numeric signal number to Signo enum. +/// Converts a numeric signal number to [`Signo`]. fn parse_signo(signo: u32) -> KResult { Signo::from_repr(signo as u8).ok_or(KError::InvalidInput) } -/// Manages the signal mask for the current thread. -/// Allows blocking/unblocking signals or replacing the entire mask. -/// Manipulate the signal mask for the current thread -/// Allows blocking, unblocking, or replacing the entire signal mask +/// Manipulates the signal mask for the current thread. +/// +/// See . pub fn sys_rt_sigprocmask( how: i32, set: *const SignalSet, @@ -57,36 +65,35 @@ pub fn sys_rt_sigprocmask( ) -> KResult { check_sigset_size(sigsetsize)?; - let curr = current(); - let sig = &curr.as_thread().signal; - // Get the current signal mask - let old = sig.blocked(); + let signal = &kthread::current_thread().signal; + let old = signal.blocked(); - // If oldset is provided, return the old mask to user space if let Some(oldset) = oldset.check_non_null() { oldset.write_vm(old)?; } - // If a new mask is provided, apply the requested operation if let Some(set) = set.check_non_null() { + // SAFETY: `read_uninit` validates that the user pointer is accessible and + // reads the bytes into a local buffer. On success the buffer is fully + // initialized, making `assume_init` sound. let set = unsafe { set.read_uninit()?.assume_init() }; - - // Apply the mask operation based on 'how' parameter let set = match how as u32 { - SIG_BLOCK => old | set, // Add signals to the mask - SIG_UNBLOCK => old & !set, // Remove signals from the mask - SIG_SETMASK => set, // Replace the entire mask + SIG_BLOCK => old | set, + SIG_UNBLOCK => old & !set, + SIG_SETMASK => set, _ => return Err(KError::InvalidInput), }; debug!("sys_rt_sigprocmask <= {set:?}"); - sig.set_blocked(set); + signal.set_blocked(set); } Ok(0) } -/// Set or retrieve the action for a signal +/// Sets or retrieves the action for a signal. +/// +/// See . pub fn sys_rt_sigaction( signo: u32, act: *const kernel_sigaction, @@ -100,12 +107,14 @@ pub fn sys_rt_sigaction( return Err(KError::InvalidInput); } - let curr = current(); - let mut actions = curr.as_thread().proc_data.signal.actions.lock(); + let current_thread = kthread::current_thread(); + let mut actions = current_thread.process_state().signal.actions.lock(); if let Some(oldact) = oldact.check_non_null() { oldact.write_vm(actions[signo].clone().into())?; } if let Some(act) = act.check_non_null() { + // SAFETY: `read_uninit` validates user-pointer accessibility and reads the + // bytes; on success the value is fully initialized. let act = unsafe { act.read_uninit()?.assume_init() }.into(); debug!("sys_rt_sigaction <= signo: {signo:?}, act: {act:?}"); actions[signo] = act; @@ -113,10 +122,12 @@ pub fn sys_rt_sigaction( Ok(0) } -/// Get the set of pending signals +/// Returns the set of pending signals. +/// +/// See . pub fn sys_rt_sigpending(set: *mut SignalSet, sigsetsize: usize) -> KResult { check_sigset_size(sigsetsize)?; - set.write_vm(current().as_thread().signal.pending())?; + set.write_vm(kthread::current_thread().signal.pending())?; Ok(0) } @@ -128,61 +139,59 @@ fn make_siginfo(signo: u32, code: i32) -> KResult> { Ok(Some(SignalInfo::new_user( signo, code, - current().as_thread().proc_data.proc.pid(), + kthread::current_thread().pid(), ))) } -/// Send a signal to a process or process group +/// Sends a signal to a process or process group. +/// +/// See . pub fn sys_kill(pid: i32, signo: u32) -> KResult { debug!("sys_kill: pid = {pid}, signo = {signo}"); let sig = make_siginfo(signo, SI_USER as _)?; match pid { - 1.. => { - send_signal_to_process(pid as _, sig)?; - } + 1.. => send_signal_to_process(pid as _, sig)?, 0 => { - let pgid = current().as_thread().proc_data.proc.group().pgid(); + let pgid = kthread::current_thread().process_state().proc.group().pgid(); send_signal_to_process_group(pgid, sig)?; } -1 => { - let curr_pid = current().as_thread().proc_data.proc.pid(); + let current_pid = kthread::current_thread().pid(); if let Some(sig) = sig { - for proc_data in processes() { - // POSIX.1 requires that kill(-1,sig) send sig to all processes that - // the calling process may send signals to, except possibly for some - // implementation-defined system processes. Linux allows a process - // to signal itself, but on Linux the call kill(-1,sig) does not - // signal the calling process. - if proc_data.proc.is_init() || proc_data.proc.pid() == curr_pid { + for proc_state in processes() { + if proc_state.proc.is_init() || proc_state.proc.pid() == current_pid { continue; } - let _ = send_signal_to_process(proc_data.proc.pid(), Some(sig.clone())); + let _ = send_signal_to_process(proc_state.proc.pid(), Some(sig.clone())); } } } - ..-1 => { - send_signal_to_process_group((-pid) as Pid, sig)?; - } + ..-1 => send_signal_to_process_group((-pid) as Pid, sig)?, } Ok(0) } -/// Send a signal to a specific thread +/// Sends a signal to a specific thread. +/// +/// See . pub fn sys_tkill(tid: Pid, signo: u32) -> KResult { let sig = make_siginfo(signo, SI_TKILL)?; send_signal_to_thread(None, tid, sig)?; Ok(0) } -/// Send a signal to a thread within a specific thread group +/// Sends a signal to a thread within a specific thread group. +/// +/// See . pub fn sys_tgkill(tgid: Pid, tid: Pid, signo: u32) -> KResult { let sig = make_siginfo(signo, SI_TKILL)?; send_signal_to_thread(Some(tgid), tid, sig)?; Ok(0) } -pub(crate) fn make_queue_signal_info( +/// Builds a queued signal payload for `rt_sigqueueinfo`-style syscalls. +pub fn make_queue_signal_info( tgid: Pid, signo: u32, sig: *const SignalInfo, @@ -192,9 +201,11 @@ pub(crate) fn make_queue_signal_info( } let signo = parse_signo(signo)?; + // SAFETY: `read_uninit` validates the user pointer and reads the data; on success + // the value is fully initialized. let mut sig = unsafe { sig.read_uninit()?.assume_init() }; sig.set_signo(signo); - if current().as_thread().proc_data.proc.pid() != tgid + if kthread::current_thread().pid() != tgid && (sig.code() >= 0 || sig.code() == SI_TKILL) { return Err(KError::OperationNotPermitted); @@ -202,7 +213,7 @@ pub(crate) fn make_queue_signal_info( Ok(Some(sig)) } -/// Queue a real-time signal with additional information to a process +/// Queues a real-time signal with additional information to a process. pub fn sys_rt_sigqueueinfo( tgid: Pid, signo: u32, @@ -216,7 +227,7 @@ pub fn sys_rt_sigqueueinfo( Ok(0) } -/// Queue a real-time signal with additional information to a specific thread +/// Queues a real-time signal with additional information to a specific thread. pub fn sys_rt_tgsigqueueinfo( tgid: Pid, tid: Pid, @@ -231,14 +242,18 @@ pub fn sys_rt_tgsigqueueinfo( Ok(0) } -/// Return from signal handler and restore context +/// Returns from a signal handler and restores context. +/// +/// See . pub fn sys_rt_sigreturn(uctx: &mut UserContext) -> KResult { block_next_signal(); - current().as_thread().signal.restore(uctx); + kthread::current_thread().signal.restore(uctx); Ok(uctx.retval() as isize) } -/// Wait for a signal from a specified set with optional timeout +/// Waits for a signal from a specified set with an optional timeout. +/// +/// See . pub fn sys_rt_sigtimedwait( uctx: &mut UserContext, set: *const SignalSet, @@ -248,9 +263,11 @@ pub fn sys_rt_sigtimedwait( ) -> KResult { check_sigset_size(sigsetsize)?; + // SAFETY: `read_uninit` validates the user pointer and reads the data; on success + // the value is fully initialized. let set = unsafe { set.read_uninit()?.assume_init() }; - let timeout = if let Some(ts) = timeout.check_non_null() { + // SAFETY: Same as above — `read_uninit` succeeded, so the value is initialized. let ts = unsafe { ts.read_uninit()?.assume_init() }; Some(ts.try_into_time_value()?) } else { @@ -259,33 +276,31 @@ pub fn sys_rt_sigtimedwait( debug!("sys_rt_sigtimedwait => set = {set:?}, timeout = {timeout:?}"); - let curr = current(); - let thr = curr.as_thread(); - let signal = &thr.signal; + let current = current(); + let current_thread = kthread::current_thread(); + let signal = ¤t_thread.signal; let old_blocked = signal.blocked(); signal.set_blocked(old_blocked & !set); uctx.set_retval(-LinuxError::EINTR.into_raw() as usize); - let fut = poll_fn(|cx| { + let wait_signal = poll_fn(|cx| { if let Some(sig) = signal.dequeue_signal(&set) { signal.set_blocked(old_blocked); Poll::Ready(Some(sig)) - } else if check_signals(thr, uctx, Some(old_blocked)) { + } else if check_signals(¤t_thread, uctx, Some(old_blocked)) { Poll::Ready(None) } else { - let _ = curr.poll_interrupt(cx); + let _ = current.poll_interrupt(cx); Poll::Pending } }); - let Ok(sig) = block_on(future::timeout(timeout, fut)) else { - // Timeout + let Ok(sig) = block_on(future::timeout(timeout, wait_signal)) else { signal.set_blocked(old_blocked); return Err(KError::WouldBlock); }; let Some(sig) = sig else { - // Interrupted return Ok(0); }; @@ -296,7 +311,9 @@ pub fn sys_rt_sigtimedwait( Ok(sig.signo() as _) } -/// Replace signal mask and suspend execution until a signal is delivered +/// Replaces the signal mask and suspends execution until a signal is delivered. +/// +/// See . pub fn sys_rt_sigsuspend( uctx: &mut UserContext, set: *const SignalSet, @@ -304,43 +321,44 @@ pub fn sys_rt_sigsuspend( ) -> KResult { check_sigset_size(sigsetsize)?; - let curr = current(); - let thr = curr.as_thread(); + let current = current(); + let current_thread = kthread::current_thread(); + // SAFETY: `read_uninit` validates the user pointer and reads the data; on success + // the value is fully initialized. let set = unsafe { set.read_uninit()?.assume_init() }; - let old_blocked = thr.signal.set_blocked(set); + let old_blocked = current_thread.signal.set_blocked(set); - // sigsuspend always returns -EINTR when a signal is caught - // We set this in uctx before check_signals so it's saved in SignalFrame uctx.set_retval(-LinuxError::EINTR.into_raw() as usize); - block_on(poll_fn(|cx| { - if check_signals(thr, uctx, Some(old_blocked)) { + if check_signals(¤t_thread, uctx, Some(old_blocked)) { return Poll::Ready(()); } - let _ = curr.poll_interrupt(cx); + let _ = current.poll_interrupt(cx); Poll::Pending })); - // sigsuspend always returns -EINTR Err(KError::Interrupted) } -/// Set or retrieve the alternate signal stack +/// Sets or retrieves the alternate signal stack. +/// +/// See . pub fn sys_sigaltstack(ss: *const SignalStack, old_ss: *mut SignalStack) -> KResult { - let curr = current(); - let sig = &curr.as_thread().signal; + let signal = &kthread::current_thread().signal; if let Some(old_ss) = old_ss.check_non_null() { - old_ss.write_vm(sig.stack())?; + old_ss.write_vm(signal.stack())?; } if let Some(ss) = ss.check_non_null() { + // SAFETY: `read_uninit` validates the user pointer and reads the data; on + // success the value is fully initialized. let ss = unsafe { ss.read_uninit()?.assume_init() }; if ss.size <= MINSIGSTKSZ as usize { return Err(KError::NoMemory); } - sig.set_stack(ss); + signal.set_stack(ss); } Ok(0) } diff --git a/posix/sync/Cargo.toml b/posix/sync/Cargo.toml new file mode 100644 index 00000000..4f5448c2 --- /dev/null +++ b/posix/sync/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "posix-sync" +description = "POSIX/Linux synchronization syscall implementations" +version.workspace = true +edition.workspace = true +authors.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +documentation.workspace = true + +[dependencies] +kerrno.workspace = true +klogger.workspace = true +ktask.workspace = true +kthread.workspace = true +linux-raw-sys.workspace = true +osvm.workspace = true +posix-types.workspace = true + diff --git a/core/ksyscall/src/sync/futex.rs b/posix/sync/src/lib.rs similarity index 59% rename from core/ksyscall/src/sync/futex.rs rename to posix/sync/src/lib.rs index 0f5fb303..a19387fc 100644 --- a/core/ksyscall/src/sync/futex.rs +++ b/posix/sync/src/lib.rs @@ -2,31 +2,32 @@ // Copyright 2025 KylinSoft Co., Ltd. // See LICENSES for license details. -//! Futex syscalls. -//! -//! This module implements fast userspace mutex (futex) operations including: -//! - Futex wait and wake operations -//! - Futex requeue operations -//! - Robust futex lists -//! - Priority-inheritance futexes - -use core::sync::atomic::Ordering; - -use kcore::{ - futex::FutexKey, - task::{AsThread, get_task}, -}; +//! POSIX/Linux synchronization syscall implementations. + +#![no_std] + +#[macro_use] +extern crate klogger; + +macro_rules! __log_prefix { + () => { + "sync: " + }; +} + +use core::{mem::size_of, sync::atomic::Ordering}; + use kerrno::{KError, KResult, LinuxError}; -use ktask::current; +use kthread::{AsThread, FutexKey, current_futex_key, get_task}; use linux_raw_sys::general::{ - FUTEX_CMD_MASK, FUTEX_CMP_REQUEUE, FUTEX_REQUEUE, FUTEX_WAIT, FUTEX_WAIT_BITSET, FUTEX_WAKE, - FUTEX_WAKE_BITSET, robust_list_head, timespec, + FUTEX_CMD_MASK, FUTEX_CMP_REQUEUE, FUTEX_PRIVATE_FLAG, FUTEX_REQUEUE, FUTEX_WAIT, + FUTEX_WAIT_BITSET, FUTEX_WAKE, FUTEX_WAKE_BITSET, robust_list_head, timespec, }; use osvm::{VirtMutPtr, VirtPtr}; -use posix_types::TimeValueLike; +use posix_types::{TimeValueLike, UserConstPtr, UserPtr}; -/// Helper to ensure a value is non-negative (unsigned interpretation) -fn assert_unsigned(value: u32) -> KResult { +/// Returns an error if the value would be negative when interpreted as signed. +fn validate_non_negative(value: u32) -> KResult { if (value as i32) < 0 { Err(KError::InvalidInput) } else { @@ -34,41 +35,49 @@ fn assert_unsigned(value: u32) -> KResult { } } +fn current_key_for_futex_op(address: usize, futex_op: u32) -> FutexKey { + if futex_op & FUTEX_PRIVATE_FLAG != 0 { + FutexKey::Private { address } + } else { + current_futex_key(address) + } +} + /// Fast userspace mutex (futex) system call. +/// /// Implements Linux futex semantics for efficient synchronization primitives. +/// See . pub fn sys_futex( - uaddr: *const u32, + uaddr: UserPtr, futex_op: u32, value: u32, - timeout: *const timespec, - uaddr2: *mut u32, + timeout_or_value2: usize, + uaddr2: UserPtr, value3: u32, ) -> KResult { debug!( - "sys_futex <= uaddr: {uaddr:?}, futex_op: {futex_op}, value: {value}, uaddr2: {uaddr2:?}, \ - value3: {value3}", + "sys_futex <= uaddr: {:?}, futex_op: {futex_op}, value: {value}, uaddr2: {:?}, value3: \ + {value3}", + uaddr.as_ptr(), + uaddr2.as_ptr(), ); - // Create a unique key for this futex (by virtual address and process) - let key = FutexKey::new_current(uaddr.addr()); + let key = current_key_for_futex_op(uaddr.as_ptr() as usize, futex_op); - let curr = current(); - let thr = curr.as_thread(); - let proc_data = &thr.proc_data; - // Get the futex table for the current process - let futex_table = proc_data.futex_table_for(&key); + let thr = kthread::current_thread(); + let proc_state = &thr.proc_state; + let futex_table = proc_state.futex_table_for(&key); - // Extract the command (lower bits) from the futex_op let command = futex_op & (FUTEX_CMD_MASK as u32); match command { FUTEX_WAIT | FUTEX_WAIT_BITSET => { - // Fast path: Check if the value at uaddr matches the expected value if uaddr.read_vm()? != value { return Err(KError::WouldBlock); } - let timeout = if let Some(ts) = timeout.check_non_null() { - // FIXME: AnyBitPattern + let timeout = if let Some(ts) = + UserConstPtr::::from(timeout_or_value2).check_non_null() + { let ts = unsafe { ts.read_uninit()?.assume_init() }.try_into_time_value()?; Some(ts) } else { @@ -111,15 +120,15 @@ pub fn sys_futex( Ok(count as _) } FUTEX_REQUEUE | FUTEX_CMP_REQUEUE => { - assert_unsigned(value)?; + validate_non_negative(value)?; if command == FUTEX_CMP_REQUEUE && uaddr.read_vm()? != value3 { return Err(KError::WouldBlock); } - let value2 = assert_unsigned(timeout.addr() as u32)?; + let value2 = validate_non_negative(timeout_or_value2 as u32)?; let futex = futex_table.get(&key); - let key2 = FutexKey::new_current(uaddr2.addr()); - let table2 = proc_data.futex_table_for(&key2); + let key2 = current_key_for_futex_op(uaddr2.as_ptr() as usize, futex_op); + let table2 = proc_state.futex_table_for(&key2); let futex2 = table2.get_or_insert(&key2); let mut count = 0; @@ -137,8 +146,8 @@ pub fn sys_futex( pub fn sys_get_robust_list( tid: u32, - head: *mut *const robust_list_head, - size: *mut usize, + head: UserPtr<*const robust_list_head>, + size: UserPtr, ) -> KResult { let task = get_task(tid)?; head.write_vm(task.as_thread().robust_list_head() as _)?; @@ -147,11 +156,12 @@ pub fn sys_get_robust_list( Ok(0) } -pub fn sys_set_robust_list(head: *const robust_list_head, size: usize) -> KResult { +pub fn sys_set_robust_list(head: UserConstPtr, size: usize) -> KResult { if size != size_of::() { return Err(KError::InvalidInput); } - current().as_thread().set_robust_list_head(head.addr()); + kthread::current_thread() + .set_robust_list_head(head.as_ptr() as usize); Ok(0) } diff --git a/process/kfd/Cargo.toml b/process/kfd/Cargo.toml new file mode 100644 index 00000000..660a7960 --- /dev/null +++ b/process/kfd/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "kfd" +description = "Process file descriptor runtime for X-Kernel" +version.workspace = true +edition.workspace = true +authors.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +documentation.workspace = true + +[dependencies] +downcast-rs.workspace = true +flatten_objects.workspace = true +fs-ng-vfs.workspace = true +kerrno.workspace = true +kio.workspace = true +kpoll.workspace = true +krlimit.workspace = true +ksync.workspace = true +ktypes.workspace = true +linux-raw-sys = { workspace = true, features = ["general"] } +unittest.workspace = true diff --git a/process/kfd/src/lib.rs b/process/kfd/src/lib.rs new file mode 100644 index 00000000..29e5e7a9 --- /dev/null +++ b/process/kfd/src/lib.rs @@ -0,0 +1,403 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2025 KylinSoft Co., Ltd. +// See LICENSES for license details. + +//! Process file-descriptor runtime. + +#![no_std] + +macro_rules! __log_prefix { + () => { + "kfd: " + }; +} + +extern crate alloc; + +use alloc::{borrow::Cow, sync::Arc, vec::Vec}; +use core::{ffi::c_int, time::Duration}; + +use downcast_rs::{DowncastSync, impl_downcast}; +use flatten_objects::FlattenObjects; +use fs_ng_vfs::DeviceId; +use kerrno::{KError, KResult}; +use kio::prelude::*; +use kpoll::Pollable; +use ksync::RwLock; +use ktypes::Once; +use linux_raw_sys::general::{ + S_IFMT, S_IFREG, STATX_ATTR_WRITE_ATOMIC, STATX_WRITE_ATOMIC, stat, statx, statx_timestamp, +}; + +/// Kernel stat structure containing file metadata. +#[derive(Debug, Clone, Copy)] +pub struct Kstat { + pub dev: u64, + pub ino: u64, + pub nlink: u32, + pub mode: u32, + pub uid: u32, + pub gid: u32, + pub size: u64, + pub blksize: u32, + pub blocks: u64, + pub rdev: DeviceId, + pub atime: Duration, + pub mtime: Duration, + pub ctime: Duration, +} + +impl Default for Kstat { + fn default() -> Self { + Self { + dev: 0, + ino: 1, + nlink: 1, + mode: 0, + uid: 1, + gid: 1, + size: 0, + blksize: 4096, + blocks: 0, + rdev: DeviceId::default(), + atime: Duration::default(), + mtime: Duration::default(), + ctime: Duration::default(), + } + } +} + +impl From for stat { + fn from(value: Kstat) -> Self { + // SAFETY: `stat` is a POD (plain-old-data) struct from `linux_raw_sys`. All-zeroes + // is a valid initial state for every field (integers become 0, pointers become null). + let mut stat: stat = unsafe { core::mem::zeroed() }; + stat.st_dev = value.dev as _; + stat.st_ino = value.ino as _; + stat.st_nlink = value.nlink as _; + stat.st_mode = value.mode as _; + stat.st_uid = value.uid as _; + stat.st_gid = value.gid as _; + stat.st_size = value.size as _; + stat.st_blksize = value.blksize as _; + stat.st_blocks = value.blocks as _; + stat.st_rdev = value.rdev.0 as _; + + stat.st_atime = value.atime.as_secs() as _; + stat.st_atime_nsec = value.atime.subsec_nanos() as _; + stat.st_mtime = value.mtime.as_secs() as _; + stat.st_mtime_nsec = value.mtime.subsec_nanos() as _; + stat.st_ctime = value.ctime.as_secs() as _; + stat.st_ctime_nsec = value.ctime.subsec_nanos() as _; + stat + } +} + +impl From for statx { + fn from(value: Kstat) -> Self { + const ATOMIC_WRITE_UNIT: u32 = 4096; + + // SAFETY: `statx` is a POD struct from `linux_raw_sys`. All-zeroes is a valid + // initial state for every field (integers become 0, reserved fields become 0). + let mut statx: statx = unsafe { core::mem::zeroed() }; + statx.stx_blksize = value.blksize as _; + statx.stx_attributes = 0; + statx.stx_attributes_mask = STATX_ATTR_WRITE_ATOMIC as _; + statx.stx_nlink = value.nlink as _; + statx.stx_uid = value.uid as _; + statx.stx_gid = value.gid as _; + statx.stx_mode = value.mode as _; + statx.stx_ino = value.ino as _; + statx.stx_size = value.size as _; + statx.stx_blocks = value.blocks as _; + statx.stx_rdev_major = value.rdev.major(); + statx.stx_rdev_minor = value.rdev.minor(); + + fn time_to_statx(time: &Duration) -> statx_timestamp { + statx_timestamp { + tv_sec: time.as_secs() as _, + tv_nsec: time.subsec_nanos() as _, + __reserved: 0, + } + } + statx.stx_atime = time_to_statx(&value.atime); + statx.stx_ctime = time_to_statx(&value.ctime); + statx.stx_mtime = time_to_statx(&value.mtime); + + statx.stx_dev_major = (value.dev >> 32) as _; + statx.stx_dev_minor = value.dev as _; + + if value.mode & S_IFMT == S_IFREG { + statx.stx_attributes |= STATX_ATTR_WRITE_ATOMIC as u64; + statx.stx_atomic_write_unit_min = ATOMIC_WRITE_UNIT; + statx.stx_atomic_write_unit_max = ATOMIC_WRITE_UNIT; + statx.stx_atomic_write_unit_max_opt = ATOMIC_WRITE_UNIT; + statx.stx_atomic_write_segments_max = 1; + statx.stx_mask |= STATX_WRITE_ATOMIC; + } + + statx + } +} + +/// Trait for types that can be used as write destinations in I/O operations. +pub trait WriteBuf: Write + IoBufMut {} +impl WriteBuf for T {} +/// I/O destination buffer type for write operations. +pub type IoDst<'a> = dyn WriteBuf + 'a; + +/// Trait for types that can be used as read sources in I/O operations. +pub trait ReadBuf: Read + IoBuf {} +impl ReadBuf for T {} +/// I/O source buffer type for read operations. +pub type IoSrc<'a> = dyn ReadBuf + 'a; + +/// Trait for file-like objects that support standard file operations. +#[allow(dead_code)] +pub trait FileLike: Pollable + DowncastSync { + fn read(&self, _dst: &mut IoDst) -> KResult { + Err(KError::InvalidInput) + } + + fn write(&self, _src: &mut IoSrc) -> KResult { + Err(KError::InvalidInput) + } + + fn stat(&self) -> KResult { + Ok(Kstat::default()) + } + + fn path(&self) -> Cow<'_, str>; + + fn ioctl(&self, _cmd: u32, _arg: usize) -> KResult { + Err(KError::NotATty) + } + + fn open_flags(&self) -> u32 { + 0 + } + + fn nonblocking(&self) -> bool { + false + } + + fn set_nonblocking(&self, _nonblocking: bool) -> KResult { + Ok(()) + } + + fn from_fd(fd: c_int) -> KResult> + where + Self: Sized + 'static, + { + get_file_like(fd)? + .downcast_arc() + .map_err(|_| KError::InvalidInput) + } + + fn add_to_fd_table(self, cloexec: bool) -> KResult + where + Self: Sized + 'static, + { + add_file_like(Arc::new(self), cloexec) + } +} +impl_downcast!(sync FileLike); + +/// A file descriptor entry in the file descriptor table. +#[derive(Clone)] +pub struct FileDescriptor { + pub inner: Arc, + pub cloexec: bool, +} + +/// The current process-local file descriptor table type. +pub type FdTable = FlattenObjects; + +static CURRENT_FD_TABLE: Once Arc>> = Once::new(); +static CURRENT_NOFILE_LIMIT: Once u64> = Once::new(); + +/// Registers current-process fd runtime accessors. +pub fn register_current_fd_runtime( + current_fd_table: fn() -> Arc>, + current_nofile_limit: fn() -> u64, +) { + CURRENT_FD_TABLE.call_once(|| current_fd_table); + CURRENT_NOFILE_LIMIT.call_once(|| current_nofile_limit); +} + +/// Creates a fresh file descriptor table. +pub fn new_fd_table() -> Arc> { + let mut table = Arc::>::new_uninit(); + Arc::get_mut(&mut table) + .expect("newly allocated Arc must be unique") + .write(RwLock::new(FlattenObjects::new())); + // SAFETY: `Arc::get_mut` above confirmed exclusive access to the inner value, and + // `write()` stored a valid `RwLock` instance. The `MaybeUninit` is now + // fully initialized. + unsafe { table.assume_init() } +} + +/// Returns the current process's file descriptor table. +pub fn current_fd_table() -> Arc> { + CURRENT_FD_TABLE + .get() + .expect("fd runtime must be registered before use")() +} + +/// Retrieves a file-like object from the file descriptor table. +pub fn get_file_like(fd: c_int) -> KResult> { + current_fd_table() + .read() + .get(fd as usize) + .map(|fd| fd.inner.clone()) + .ok_or(KError::BadFileDescriptor) +} + +/// Adds a file-like object to the current process's file descriptor table. +pub fn add_file_like(f: Arc, cloexec: bool) -> KResult { + let max_nofile = CURRENT_NOFILE_LIMIT + .get() + .expect("fd runtime must be registered before use")(); + let fd_table = current_fd_table(); + let mut table = fd_table.write(); + if table.count() as u64 >= max_nofile { + return Err(KError::TooManyOpenFiles); + } + let fd = FileDescriptor { inner: f, cloexec }; + Ok(table.add(fd).map_err(|_| KError::TooManyOpenFiles)? as c_int) +} + +/// Closes a file descriptor and removes it from the file descriptor table. +pub fn close_file_like(fd: c_int) -> KResult { + current_fd_table() + .write() + .remove(fd as usize) + .ok_or(KError::BadFileDescriptor)?; + Ok(()) +} + +/// Close all open file descriptors for the current process. +pub fn close_all_fds() { + let fd_table = current_fd_table(); + if Arc::strong_count(&fd_table) > 1 { + return; + } + + let mut table = fd_table.write(); + let ids: Vec = table.ids().collect(); + let mut removed = Vec::with_capacity(ids.len()); + for id in ids { + if let Some(fd) = table.remove(id) { + removed.push(fd); + } + } + drop(table); + drop(removed); +} + +#[cfg(unittest)] +mod tests { + use unittest::def_test; + + use super::*; + + #[def_test] + fn test_kstat_default() { + let kstat = Kstat::default(); + assert_eq!(kstat.dev, 0); + assert_eq!(kstat.ino, 1); + assert_eq!(kstat.nlink, 1); + assert_eq!(kstat.mode, 0); + assert_eq!(kstat.uid, 1); + assert_eq!(kstat.gid, 1); + assert_eq!(kstat.size, 0); + assert_eq!(kstat.blksize, 4096); + assert_eq!(kstat.blocks, 0); + assert_eq!(kstat.atime, Duration::default()); + assert_eq!(kstat.mtime, Duration::default()); + assert_eq!(kstat.ctime, Duration::default()); + } + + #[def_test] + fn test_kstat_to_stat() { + let kstat = Kstat { + dev: 42, + ino: 100, + nlink: 3, + mode: 0o755, + uid: 1000, + gid: 1000, + size: 4096, + blksize: 512, + blocks: 8, + rdev: DeviceId::default(), + atime: Duration::new(1000, 500_000_000), + mtime: Duration::new(2000, 0), + ctime: Duration::new(3000, 123_456_789), + }; + + let s: stat = kstat.into(); + assert_eq!(s.st_dev, 42); + assert_eq!(s.st_ino, 100); + assert_eq!(s.st_nlink, 3); + assert_eq!(s.st_mode, 0o755); + assert_eq!(s.st_uid, 1000); + assert_eq!(s.st_gid, 1000); + assert_eq!(s.st_size, 4096); + assert_eq!(s.st_blksize, 512); + assert_eq!(s.st_blocks, 8); + assert_eq!(s.st_atime, 1000); + assert_eq!(s.st_atime_nsec, 500_000_000); + assert_eq!(s.st_mtime, 2000); + assert_eq!(s.st_mtime_nsec, 0); + assert_eq!(s.st_ctime, 3000); + assert_eq!(s.st_ctime_nsec, 123_456_789); + } + + #[def_test] + fn test_kstat_to_statx() { + let kstat = Kstat { + dev: (5u64 << 32) | 10, + ino: 200, + nlink: 2, + mode: 0o644, + uid: 500, + gid: 500, + size: 8192, + blksize: 4096, + blocks: 16, + rdev: DeviceId::default(), + atime: Duration::new(100, 999_999_999), + mtime: Duration::new(200, 0), + ctime: Duration::new(300, 1), + }; + + let sx: statx = kstat.into(); + assert_eq!(sx.stx_ino, 200); + assert_eq!(sx.stx_nlink, 2); + assert_eq!(sx.stx_mode, 0o644); + assert_eq!(sx.stx_uid, 500); + assert_eq!(sx.stx_gid, 500); + assert_eq!(sx.stx_size, 8192); + assert_eq!(sx.stx_blksize, 4096); + assert_eq!(sx.stx_blocks, 16); + assert_eq!(sx.stx_dev_major, 5); + assert_eq!(sx.stx_dev_minor, 10); + assert_eq!(sx.stx_atime.tv_sec, 100); + assert_eq!(sx.stx_atime.tv_nsec, 999_999_999); + assert_eq!(sx.stx_mtime.tv_sec, 200); + assert_eq!(sx.stx_mtime.tv_nsec, 0); + assert_eq!(sx.stx_ctime.tv_sec, 300); + assert_eq!(sx.stx_ctime.tv_nsec, 1); + } + + #[def_test] + fn test_kstat_default_to_stat_zeroed() { + let s: stat = Kstat::default().into(); + assert_eq!(s.st_dev, 0); + assert_eq!(s.st_size, 0); + assert_eq!(s.st_blksize, 4096); + assert_eq!(s.st_ino, 1); + assert_eq!(s.st_nlink, 1); + } +} diff --git a/process/kfutex/Cargo.toml b/process/kfutex/Cargo.toml new file mode 100644 index 00000000..f87b2107 --- /dev/null +++ b/process/kfutex/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "kfutex" +description = "Process-side futex primitives for X-Kernel" +version.workspace = true +edition.workspace = true +authors.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +documentation.workspace = true + +[dependencies] +hashbrown.workspace = true +kerrno.workspace = true +kspin.workspace = true +ksync.workspace = true +ktask.workspace = true +memaddr.workspace = true +memspace.workspace = true +memspace_file.workspace = true +unittest.workspace = true + diff --git a/core/kcore/src/futex.rs b/process/kfutex/src/lib.rs similarity index 94% rename from core/kcore/src/futex.rs rename to process/kfutex/src/lib.rs index aefccaeb..c560820c 100644 --- a/core/kcore/src/futex.rs +++ b/process/kfutex/src/lib.rs @@ -2,7 +2,17 @@ // Copyright 2025 KylinSoft Co., Ltd. // See LICENSES for license details. -//! Futex implementation. +//! Process-side futex primitives. + +#![no_std] + +macro_rules! __log_prefix { + () => { + "kfutex: " + }; +} + +extern crate alloc; use alloc::{ collections::vec_deque::VecDeque, @@ -21,10 +31,7 @@ use hashbrown::HashMap; use kerrno::KResult; use kspin::SpinNoIrq; use ksync::Mutex; -use ktask::{ - current, - future::{self, block_on, interruptible}, -}; +use ktask::future::{self, block_on, interruptible}; use memaddr::VirtAddr; use memspace::{ AddrSpace, @@ -32,13 +39,12 @@ use memspace::{ }; use memspace_file::FileBackend; -use crate::task::AsThread; - /// Wait queue used by futex. #[derive(Default)] pub struct WaitQueue { queue: SpinNoIrq>, } + impl WaitQueue { /// Creates a new `WaitQueue`. pub fn new() -> Self { @@ -158,11 +164,6 @@ impl FutexKey { Self::Private { address } } - /// Shortcut to create a `FutexKey` for the current task's address space. - pub fn new_current(address: usize) -> Self { - Self::new(¤t().as_thread().proc_data.aspace.lock(), address) - } - fn as_usize(&self) -> usize { match self { FutexKey::Private { address } => *address, @@ -215,8 +216,8 @@ impl FutexTable { }) } - /// Gets the wait queue associated with the given address, or inserts a a - /// new one if it doesn't exist. + /// Gets the wait queue associated with the given address, or inserts a new + /// one if it doesn't exist. pub fn get_or_insert(&self, key: &FutexKey) -> FutexGuard<'_> { let key = key.as_usize(); let mut table = self.0.lock(); @@ -254,9 +255,8 @@ impl Drop for FutexGuard<'_> { } } -/// Unit tests. #[cfg(unittest)] -pub mod tests_futex { +mod tests_futex { use unittest::def_test; use super::*; diff --git a/process/kprocess/Cargo.toml b/process/kprocess/Cargo.toml index 0ab39cd0..5022f7c1 100644 --- a/process/kprocess/Cargo.toml +++ b/process/kprocess/Cargo.toml @@ -10,8 +10,8 @@ description = "Process management for X-Kernel" [dependencies] kspin = { workspace = true } -lazyinit = "0.2.1" -weak-map = "0.1" +lazyinit.workspace = true +weak-map.workspace = true unittest.workspace = true [dev-dependencies] diff --git a/process/kresources/Cargo.toml b/process/kresources/Cargo.toml new file mode 100644 index 00000000..d1b7cc3b --- /dev/null +++ b/process/kresources/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "kresources" +description = "Process resource owners for X-Kernel" +version.workspace = true +edition.workspace = true +authors.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +documentation.workspace = true + +[dependencies] +kfd.workspace = true +krlimit.workspace = true +ksync.workspace = true +linux-raw-sys = { workspace = true, features = ["general"] } +unittest.workspace = true diff --git a/process/kresources/src/lib.rs b/process/kresources/src/lib.rs new file mode 100644 index 00000000..e4f1056d --- /dev/null +++ b/process/kresources/src/lib.rs @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2025 KylinSoft Co., Ltd. +// See LICENSES for license details. + +//! Process-owned resource objects. + +#![no_std] + +macro_rules! __log_prefix { + () => { + "kresources: " + }; +} + +extern crate alloc; + +use alloc::sync::Arc; + +use kfd::{FdTable, new_fd_table}; +use krlimit::Rlimits; +use ksync::RwLock; +use linux_raw_sys::general::RLIMIT_NOFILE; + +/// Process-owned resource state. +/// +/// This is the first owner boundary for process resources. More resource handles +/// can move here later; for now it owns the rlimit set explicitly. +pub struct ProcessResources { + /// Per-process resource limits. + pub rlimits: RwLock, + /// The process-owned file descriptor table handle. + fd_table: RwLock>>, +} + +impl ProcessResources { + /// Creates a new process resource set with default limits. + pub fn new(user_stack_size: usize) -> Arc { + Arc::new(Self { + rlimits: RwLock::new(Rlimits::new(user_stack_size)), + fd_table: RwLock::new(new_fd_table()), + }) + } + + /// Returns the current RLIMIT_NOFILE soft cap. + pub fn max_nofile(&self) -> u64 { + self.rlimits.read()[RLIMIT_NOFILE].current + } + + /// Returns the current file descriptor table handle. + pub fn fd_table(&self) -> Arc> { + self.fd_table.read().clone() + } + + /// Replaces the file descriptor table handle. + pub fn replace_fd_table(&self, table: Arc>) -> Arc> { + core::mem::replace(&mut *self.fd_table.write(), table) + } +} + +#[cfg(unittest)] +mod tests { + use unittest::def_test; + + use super::ProcessResources; + + #[def_test] + fn test_process_resources_default_limits() { + let resources = ProcessResources::new(0x80000); + assert_eq!(resources.max_nofile(), 1024); + assert_eq!(resources.rlimits.read()[linux_raw_sys::general::RLIMIT_STACK].current, 0x80000); + assert_eq!(resources.fd_table().read().count(), 0); + } +} diff --git a/process/krlimit/Cargo.toml b/process/krlimit/Cargo.toml new file mode 100644 index 00000000..d77155b6 --- /dev/null +++ b/process/krlimit/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "krlimit" +description = "Process resource limit types for X-Kernel" +version.workspace = true +edition.workspace = true +authors.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +documentation.workspace = true + +[dependencies] +linux-raw-sys.workspace = true +unittest.workspace = true + diff --git a/core/kcore/src/resources.rs b/process/krlimit/src/lib.rs similarity index 67% rename from core/kcore/src/resources.rs rename to process/krlimit/src/lib.rs index c1bc9e38..f1a1ef17 100644 --- a/core/kcore/src/resources.rs +++ b/process/krlimit/src/lib.rs @@ -2,21 +2,29 @@ // Copyright 2025 KylinSoft Co., Ltd. // See LICENSES for license details. -//! Resource limits. +//! Process resource-limit types. + +#![no_std] + +macro_rules! __log_prefix { + () => { + "krlimit: " + }; +} use core::ops::{Index, IndexMut}; use linux_raw_sys::general::{RLIM_NLIMITS, RLIMIT_NOFILE, RLIMIT_STACK}; -/// The maximum number of open files +/// The maximum number of open files. pub const FILE_LIMIT: usize = 1024; -/// The limit for a specific resource +/// The limit for a specific resource. #[derive(Default)] pub struct Rlimit { - /// The current limit for the resource (soft) + /// The current limit for the resource (soft). pub current: u64, - /// The maximum limit for the resource (hard) + /// The maximum limit for the resource (hard). pub max: u64, } @@ -39,13 +47,14 @@ impl From for Rlimit { } } -/// Process resource limits +/// Process resource limits. pub struct Rlimits([Rlimit; RLIM_NLIMITS as usize]); -impl Default for Rlimits { - fn default() -> Self { +impl Rlimits { + /// Creates a new limit table with the default stack and file-descriptor caps. + pub fn new(user_stack_size: usize) -> Self { let mut result = Self(Default::default()); - result[RLIMIT_STACK] = (crate::config::USER_STACK_SIZE as u64).into(); + result[RLIMIT_STACK] = (user_stack_size as u64).into(); result[RLIMIT_NOFILE] = (FILE_LIMIT as u64).into(); result } @@ -65,9 +74,8 @@ impl IndexMut for Rlimits { } } -/// Unit tests. #[cfg(unittest)] -pub mod tests_resources { +mod tests { use unittest::def_test; use super::*; @@ -87,35 +95,26 @@ pub mod tests_resources { } #[def_test] - fn test_rlimits_default() { - let limits = Rlimits::default(); - assert_eq!( - limits[RLIMIT_STACK].current, - crate::config::USER_STACK_SIZE as u64 - ); + fn test_rlimits_new() { + let limits = Rlimits::new(0x80000); + assert_eq!(limits[RLIMIT_STACK].current, 0x80000); assert_eq!(limits[RLIMIT_NOFILE].current, FILE_LIMIT as u64); } #[def_test] fn test_rlimits_index_mut_updates_selected_limit() { - let mut limits = Rlimits::default(); + let mut limits = Rlimits::new(0x80000); limits[RLIMIT_NOFILE] = Rlimit::new(128, 256); assert_eq!(limits[RLIMIT_NOFILE].current, 128); assert_eq!(limits[RLIMIT_NOFILE].max, 256); - assert_eq!( - limits[RLIMIT_STACK].current, - crate::config::USER_STACK_SIZE as u64 - ); + assert_eq!(limits[RLIMIT_STACK].current, 0x80000); } #[def_test] - fn test_rlimits_preserve_stack_max_default() { - let limits = Rlimits::default(); - assert_eq!( - limits[RLIMIT_STACK].max, - crate::config::USER_STACK_SIZE as u64 - ); + fn test_rlimits_preserve_defaults() { + let limits = Rlimits::new(0x80000); + assert_eq!(limits[RLIMIT_STACK].max, 0x80000); assert_eq!(limits[RLIMIT_NOFILE].max, FILE_LIMIT as u64); } } diff --git a/process/kthread/Cargo.toml b/process/kthread/Cargo.toml new file mode 100644 index 00000000..7b7eb765 --- /dev/null +++ b/process/kthread/Cargo.toml @@ -0,0 +1,37 @@ +[package] +name = "kthread" +description = "Process-side thread/runtime surface for X-Kernel" +version.workspace = true +edition.workspace = true +authors.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true + +[features] +tee = ["dep:tee_task_iface"] +tee_ta_sign = [] + +[dependencies] +extern-trait.workspace = true +hashbrown.workspace = true +kcred.workspace = true +kerrno.workspace = true +kfd.workspace = true +klogger.workspace = true +kpoll.workspace = true +kfutex.workspace = true +kprocess.workspace = true +krlimit.workspace = true +kresources.workspace = true +ksignal.workspace = true +ksync.workspace = true +ktask.workspace = true +ktimer.workspace = true +ktypes.workspace = true +lazy_static.workspace = true +memspace.workspace = true +scope-local.workspace = true +tee_task_iface = { workspace = true, optional = true } +unittest.workspace = true +weak-map.workspace = true diff --git a/process/kthread/src/lib.rs b/process/kthread/src/lib.rs new file mode 100644 index 00000000..4f34b845 --- /dev/null +++ b/process/kthread/src/lib.rs @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2025 KylinSoft Co., Ltd. +// See LICENSES for license details. + +//! Process-side thread/runtime surface. +//! +//! This crate owns the process-side thread/runtime surface: +//! thread state, shared process state, task registries, and signal-delivery +//! glue. `kcore::task` now re-exports this surface instead of owning the +//! implementation directly. + +#![no_std] + +macro_rules! __log_prefix { + () => { + "kthread: " + }; +} + +extern crate alloc; + +#[macro_use] +extern crate klogger; + +mod lifecycle_state; +mod process_state; +mod posix_state; +mod registry; +mod runtime_state; +mod signal; +mod stat; +mod timer; +mod thread; + +pub use kfutex::{FutexEntry, FutexGuard, FutexKey, FutexTable, WaitQueue}; +pub use krlimit::{FILE_LIMIT, Rlimit, Rlimits}; +pub use kresources::ProcessResources; +pub use lifecycle_state::ProcessLifecycleState; +pub use process_state::{ProcessState, ProcessStateConfig}; +pub use posix_state::ProcessPosixState; +pub use registry::{ + add_task_to_table, cleanup_task_tables, get_process_group, get_process_state, get_session, + get_task, processes, tasks, +}; +pub use runtime_state::ProcessRuntimeState; +pub use signal::{ + poll_timer, send_signal_to_process, send_signal_to_process_group, send_signal_to_thread, + set_timer_state, +}; +pub use stat::TaskStat; +#[cfg(feature = "tee")] +pub use tee_task_iface::{TeeSessionCtxTrait, TeeTaCtx}; +pub use thread::{ + AsThread, AssumeSync, CurrentThread, Thread, current_process_state, current_thread, + with_current_thread, +}; +pub use timer::{TimeManager, TimerState, spawn_alarm_task}; + +/// Builds a futex key in the context of the current process address space. +pub fn current_futex_key(address: usize) -> FutexKey { + let proc_state = current_process_state(); + let aspace = proc_state.address_space().lock(); + FutexKey::new(&aspace, address) +} diff --git a/process/kthread/src/lifecycle_state.rs b/process/kthread/src/lifecycle_state.rs new file mode 100644 index 00000000..c4b367ec --- /dev/null +++ b/process/kthread/src/lifecycle_state.rs @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2025 KylinSoft Co., Ltd. +// See LICENSES for license details. + +//! Lifecycle state shared by all threads in a process. + +use alloc::sync::Arc; + +use kpoll::PollSet; + +/// Process lifecycle state shared by all threads in a process. +#[derive(Default)] +pub struct ProcessLifecycleState { + child_exit_event: Arc, + exit_event: Arc, +} + +impl ProcessLifecycleState { + /// Creates a new [`ProcessLifecycleState`]. + pub fn new() -> Self { + Self { + child_exit_event: Arc::default(), + exit_event: Arc::default(), + } + } + + /// Returns the child-exit wait event. + pub fn child_exit_event(&self) -> &Arc { + &self.child_exit_event + } + + /// Returns the process-exit event. + pub fn exit_event(&self) -> &Arc { + &self.exit_event + } +} diff --git a/process/kthread/src/posix_state.rs b/process/kthread/src/posix_state.rs new file mode 100644 index 00000000..cf3b9191 --- /dev/null +++ b/process/kthread/src/posix_state.rs @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2025 KylinSoft Co., Ltd. +// See LICENSES for license details. + +//! POSIX-facing process-shared state. + +use alloc::{ + string::String, + sync::Arc, + vec::Vec, +}; +use core::sync::atomic::{AtomicU32, Ordering}; + +use ksignal::Signo; +use ksync::RwLock; + +/// POSIX-facing state shared by all threads in a process. +pub struct ProcessPosixState { + exe_path: RwLock, + cmdline: RwLock>>, + exit_signal: Option, + umask: AtomicU32, +} + +impl ProcessPosixState { + /// Creates a new [`ProcessPosixState`]. + pub fn new(exe_path: String, cmdline: Arc>, exit_signal: Option) -> Self { + Self { + exe_path: RwLock::new(exe_path), + cmdline: RwLock::new(cmdline), + exit_signal, + umask: AtomicU32::new(0o022), + } + } + + /// Returns the executable path. + pub fn exe_path(&self) -> &RwLock { + &self.exe_path + } + + /// Returns the command-line arguments. + pub fn cmdline(&self) -> &RwLock>> { + &self.cmdline + } + + /// Returns the process exit signal. + pub fn exit_signal(&self) -> Option { + self.exit_signal + } + + /// Returns the process umask. + pub fn umask(&self) -> u32 { + self.umask.load(Ordering::SeqCst) + } + + /// Sets the process umask. + pub fn set_umask(&self, umask: u32) { + self.umask.store(umask, Ordering::SeqCst); + } + + /// Sets the process umask and returns the old value. + pub fn replace_umask(&self, umask: u32) -> u32 { + self.umask.swap(umask, Ordering::SeqCst) + } +} diff --git a/process/kthread/src/process_state.rs b/process/kthread/src/process_state.rs new file mode 100644 index 00000000..d1115474 --- /dev/null +++ b/process/kthread/src/process_state.rs @@ -0,0 +1,275 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2025 KylinSoft Co., Ltd. +// See LICENSES for license details. + +use alloc::{ + string::String, + sync::{Arc, Weak}, + vec::Vec, +}; + +use hashbrown::HashMap; +use kfd::{FdTable, register_current_fd_runtime}; +use kfutex::{FutexKey, FutexTable}; +use kcred::Credentials; +use kpoll::PollSet; +use kprocess::Process; +use kresources::ProcessResources; +use ksignal::{ + Signo, + api::{ProcessSignalManager, SignalActions}, +}; +use ksync::{Mutex, RwLock, spin::SpinNoIrq}; +use lazy_static::lazy_static; +#[cfg(feature = "tee")] +use tee_task_iface::TeeTaCtx; + +use crate::{ProcessLifecycleState, ProcessRuntimeState, posix_state::ProcessPosixState}; + +/// Static configuration used to initialize a [`ProcessState`]. +#[derive(Clone, Copy)] +pub struct ProcessStateConfig { + /// Initial user heap base address. + pub user_heap_base: usize, + /// Default user stack size limit. + pub user_stack_size: usize, + /// Signal trampoline entry address. + pub signal_trampoline: usize, +} + +/// [`Process`]-shared state. +pub struct ProcessState { + /// The process. + pub proc: Arc, + + /// The process-owned resource state. + pub resources: Arc, + + /// The POSIX-facing shared state. + posix: ProcessPosixState, + + /// The lifecycle state shared by all threads in the process. + lifecycle: ProcessLifecycleState, + + /// The runtime state shared by all threads in the process. + runtime: ProcessRuntimeState, + + /// The process signal manager. + pub signal: Arc, + + /// The private futex table. + futex_table: Arc, + + /// POSIX credentials shared by all threads in this process. + pub credentials: RwLock, + + /// The TEE TA context. + #[cfg(feature = "tee")] + pub tee_ta_ctx: RwLock, +} + +fn current_fd_table() -> Arc> { + crate::current_process_state().resources.fd_table() +} + +fn current_nofile_limit() -> u64 { + crate::current_process_state().resources.max_nofile() +} + +impl ProcessState { + /// Creates a new [`ProcessState`]. + #[allow(clippy::too_many_arguments)] + pub fn new( + proc: Arc, + exe_path: String, + cmdline: Arc>, + address_space: Arc>, + signal_actions: Arc>, + exit_signal: Option, + credentials: Credentials, + config: ProcessStateConfig, + ) -> Arc { + register_current_fd_runtime(current_fd_table, current_nofile_limit); + #[cfg(feature = "tee")] + let tee_ta_ctx = RwLock::new(TeeTaCtx::new(&exe_path)); + let posix = ProcessPosixState::new(exe_path, cmdline, exit_signal); + let runtime = ProcessRuntimeState::new(address_space, config.user_heap_base); + Arc::new(Self { + proc, + #[cfg(feature = "tee")] + tee_ta_ctx, + resources: ProcessResources::new(config.user_stack_size), + posix, + lifecycle: ProcessLifecycleState::new(), + runtime, + + signal: Arc::new(ProcessSignalManager::new( + signal_actions, + config.signal_trampoline, + )), + + futex_table: Arc::new(FutexTable::new()), + credentials: RwLock::new(credentials), + }) + } + + /// Returns whether this process behaves like a clone child. + pub fn is_clone_child(&self) -> bool { + self.exit_signal() != Some(Signo::SIGCHLD) + } + + /// Returns the futex table for the given key. + pub fn futex_table_for(&self, key: &FutexKey) -> Arc { + match key { + FutexKey::Private { .. } => self.futex_table.clone(), + FutexKey::Shared { region, .. } => { + let ptr = match region { + Ok(pages) => Weak::as_ptr(pages) as usize, + Err(key) => Weak::as_ptr(key) as usize, + }; + SHARED_FUTEX_TABLES.lock().get_or_insert(ptr) + } + } + } + + /// Returns the POSIX-facing shared state. + pub fn posix(&self) -> &ProcessPosixState { + &self.posix + } + + /// Returns the lifecycle state shared by all threads in the process. + pub fn lifecycle(&self) -> &ProcessLifecycleState { + &self.lifecycle + } + + /// Returns the runtime state shared by all threads in the process. + pub fn runtime(&self) -> &ProcessRuntimeState { + &self.runtime + } + + /// Returns the executable path. + pub fn exe_path(&self) -> &RwLock { + self.posix.exe_path() + } + + /// Returns the command-line arguments. + pub fn cmdline(&self) -> &RwLock>> { + self.posix.cmdline() + } + + /// Returns the process exit signal. + pub fn exit_signal(&self) -> Option { + self.posix.exit_signal() + } + + /// Returns the process umask. + pub fn umask(&self) -> u32 { + self.posix.umask() + } + + /// Sets the process umask. + pub fn set_umask(&self, umask: u32) { + self.posix.set_umask(umask); + } + + /// Sets the process umask and returns the old value. + pub fn replace_umask(&self, umask: u32) -> u32 { + self.posix.replace_umask(umask) + } + + /// Returns the child-exit wait event. + pub fn child_exit_event(&self) -> &Arc { + self.lifecycle.child_exit_event() + } + + /// Returns the process-exit event. + pub fn exit_event(&self) -> &Arc { + self.lifecycle.exit_event() + } + + /// Returns the virtual address space. + pub fn address_space(&self) -> &Arc> { + self.runtime.address_space() + } + + /// Returns the process-local scope. + pub fn scope(&self) -> &RwLock { + self.runtime.scope() + } + + /// Returns the top address of the user heap. + pub fn heap_top(&self) -> usize { + self.runtime.heap_top() + } + + /// Sets the top address of the user heap. + pub fn set_heap_top(&self, top: usize) { + self.runtime.set_heap_top(top); + } +} + +pub(super) struct FutexTables { + pub(super) map: HashMap>, + pub(super) operations: usize, +} + +impl FutexTables { + pub(super) fn new() -> Self { + Self { + map: HashMap::new(), + operations: 0, + } + } + + pub(super) fn get_or_insert(&mut self, key: usize) -> Arc { + self.operations += 1; + if self.operations == 100 { + self.operations = 0; + self.map + .retain(|_, table| Arc::strong_count(table) > 1 || !table.is_empty()); + } + self.map + .entry(key) + .or_insert_with(|| Arc::new(FutexTable::new())) + .clone() + } +} + +lazy_static! { + static ref SHARED_FUTEX_TABLES: Mutex = Mutex::new(FutexTables::new()); +} + +#[cfg(unittest)] +mod tests_process_state { + use alloc::sync::Arc; + + use unittest::def_test; + + use super::FutexTables; + + #[def_test] + fn test_futextables_get_or_insert_reuses_existing_table() { + let mut tables = FutexTables::new(); + let first = tables.get_or_insert(0x1234); + let second = tables.get_or_insert(0x1234); + + assert!(Arc::ptr_eq(&first, &second)); + assert_eq!(tables.map.len(), 1); + assert_eq!(tables.operations, 2); + } + + #[def_test] + fn test_futextables_cleanup_drops_stale_entries_on_threshold() { + let mut tables = FutexTables::new(); + let stale = tables.get_or_insert(1); + drop(stale); + + tables.operations = 99; + let fresh = tables.get_or_insert(2); + + assert_eq!(tables.operations, 0); + assert!(!tables.map.contains_key(&1)); + assert!(tables.map.contains_key(&2)); + assert_eq!(Arc::strong_count(&fresh), 2); + } +} diff --git a/process/kthread/src/registry.rs b/process/kthread/src/registry.rs new file mode 100644 index 00000000..2e080769 --- /dev/null +++ b/process/kthread/src/registry.rs @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2025 KylinSoft Co., Ltd. +// See LICENSES for license details. + +use alloc::{ + sync::{Arc, Weak}, + vec::Vec, +}; + +use kerrno::{KError, KResult}; +use kprocess::{Pid, ProcessGroup, Session}; +use ksync::RwLock; +use ktask::{KtaskRef, WeakKtaskRef, current}; +use weak_map::WeakMap; + +use crate::{AsThread, ProcessState, current_process_state}; + +static TASK_TABLE: RwLock> = RwLock::new(WeakMap::new()); +static PROCESS_TABLE: RwLock>> = RwLock::new(WeakMap::new()); +static PROCESS_GROUP_TABLE: RwLock>> = RwLock::new(WeakMap::new()); +static SESSION_TABLE: RwLock>> = RwLock::new(WeakMap::new()); + +/// Cleans up expired entries in the task tables. +pub fn cleanup_task_tables() { + TASK_TABLE.write().cleanup(); + PROCESS_TABLE.write().cleanup(); + PROCESS_GROUP_TABLE.write().cleanup(); + SESSION_TABLE.write().cleanup(); +} + +/// Adds the task, process, process group, and session to the corresponding tables. +pub fn add_task_to_table(task: &KtaskRef) { + let tid = task.id().as_u64() as Pid; + + let mut task_table = TASK_TABLE.write(); + task_table.insert(tid, task); + + let proc_state = &task.as_thread().proc_state; + let proc = &proc_state.proc; + let pid = proc.pid(); + let mut proc_table = PROCESS_TABLE.write(); + if proc_table.contains_key(&pid) { + return; + } + proc_table.insert(pid, proc_state); + + let pg = proc.group(); + let mut pg_table = PROCESS_GROUP_TABLE.write(); + if pg_table.contains_key(&pg.pgid()) { + return; + } + pg_table.insert(pg.pgid(), &pg); + + let session = pg.session(); + let mut session_table = SESSION_TABLE.write(); + if session_table.contains_key(&session.sid()) { + return; + } + session_table.insert(session.sid(), &session); +} + +/// Lists all tasks. +pub fn tasks() -> Vec { + TASK_TABLE.read().values().collect() +} + +/// Finds the task with the given TID. +pub fn get_task(tid: Pid) -> KResult { + if tid == 0 { + return Ok(current().clone()); + } + TASK_TABLE.read().get(&tid).ok_or(KError::NoSuchProcess) +} + +/// Lists all processes. +pub fn processes() -> Vec> { + PROCESS_TABLE.read().values().collect() +} + +/// Finds the process with the given PID. +pub fn get_process_state(pid: Pid) -> KResult> { + if pid == 0 { + return Ok(current_process_state()); + } + PROCESS_TABLE.read().get(&pid).ok_or(KError::NoSuchProcess) +} + +/// Finds the process group with the given PGID. +pub fn get_process_group(pgid: Pid) -> KResult> { + PROCESS_GROUP_TABLE + .read() + .get(&pgid) + .ok_or(KError::NoSuchProcess) +} + +/// Finds the session with the given SID. +pub fn get_session(sid: Pid) -> KResult> { + SESSION_TABLE.read().get(&sid).ok_or(KError::NoSuchProcess) +} diff --git a/process/kthread/src/runtime_state.rs b/process/kthread/src/runtime_state.rs new file mode 100644 index 00000000..08c046b4 --- /dev/null +++ b/process/kthread/src/runtime_state.rs @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2025 KylinSoft Co., Ltd. +// See LICENSES for license details. + +//! Runtime state shared by all threads in a process. + +use alloc::sync::Arc; +use core::sync::atomic::{AtomicUsize, Ordering}; + +use ksync::{Mutex, RwLock}; +use memspace::AddrSpace; +use scope_local::Scope; + +/// Process runtime state shared by all threads in a process. +pub struct ProcessRuntimeState { + address_space: Arc>, + scope: RwLock, + heap_top: AtomicUsize, +} + +impl ProcessRuntimeState { + /// Creates a new [`ProcessRuntimeState`]. + pub fn new(address_space: Arc>, user_heap_base: usize) -> Self { + Self { + address_space, + scope: RwLock::new(Scope::new()), + heap_top: AtomicUsize::new(user_heap_base), + } + } + + /// Returns the virtual address space. + pub fn address_space(&self) -> &Arc> { + &self.address_space + } + + /// Returns the process-local scope. + pub fn scope(&self) -> &RwLock { + &self.scope + } + + /// Returns the top address of the user heap. + pub fn heap_top(&self) -> usize { + self.heap_top.load(Ordering::Acquire) + } + + /// Sets the top address of the user heap. + pub fn set_heap_top(&self, top: usize) { + self.heap_top.store(top, Ordering::Release) + } +} diff --git a/process/kthread/src/signal.rs b/process/kthread/src/signal.rs new file mode 100644 index 00000000..594e1f3d --- /dev/null +++ b/process/kthread/src/signal.rs @@ -0,0 +1,152 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2025 KylinSoft Co., Ltd. +// See LICENSES for license details. + +use kerrno::{KError, KResult}; +use kprocess::Pid; +use ksignal::SignalInfo; +use ktask::TaskInner; +use ktimer::TimerState; + +use crate::{AsThread, Thread, get_process_group, get_process_state, get_task}; + +/// Polls the timer for a task. +pub fn poll_timer(task: &TaskInner) { + let Some(thread) = task.try_as_thread() else { + return; + }; + let Ok(mut time) = thread.time.try_borrow_mut() else { + return; + }; + time.poll(|signo| { + send_signal_thread_inner(task, thread, SignalInfo::new_kernel(signo)); + }); +} + +/// Sets the timer state for a task. +pub fn set_timer_state(task: &TaskInner, state: TimerState) { + let Some(thread) = task.try_as_thread() else { + return; + }; + let Ok(mut time) = thread.time.try_borrow_mut() else { + return; + }; + time.poll(|signo| { + send_signal_thread_inner(task, thread, SignalInfo::new_kernel(signo)); + }); + time.set_state(state); +} + +fn send_signal_thread_inner(task: &TaskInner, thread: &Thread, sig: SignalInfo) { + if thread.signal.send_signal(sig) { + task.interrupt(); + } +} + +/// Sends a signal to a thread. +pub fn send_signal_to_thread(tgid: Option, tid: Pid, sig: Option) -> KResult<()> { + let task = get_task(tid)?; + let thread = task.try_as_thread().ok_or(KError::OperationNotPermitted)?; + if tgid.is_some_and(|tgid| thread.proc_state.proc.pid() != tgid) { + return Err(KError::NoSuchProcess); + } + + if let Some(sig) = sig { + debug!("Send signal {:?} to thread {}", sig.signo(), tid); + send_signal_thread_inner(&task, thread, sig); + } + + Ok(()) +} + +/// Sends a signal to a process. +pub fn send_signal_to_process(pid: Pid, sig: Option) -> KResult<()> { + let proc_state = get_process_state(pid)?; + + if let Some(sig) = sig { + let signo = sig.signo(); + debug!("Send signal {signo:?} to process {pid}"); + if let Some(tid) = proc_state.signal.send_signal(sig) + && let Ok(task) = get_task(tid) + { + task.interrupt(); + } + } + + Ok(()) +} + +/// Sends a signal to a process group. +pub fn send_signal_to_process_group(pgid: Pid, sig: Option) -> KResult<()> { + let pg = get_process_group(pgid)?; + + if let Some(sig) = sig { + info!("Send signal {:?} to process group {}", sig.signo(), pgid); + for proc in pg.processes() { + send_signal_to_process(proc.pid(), Some(sig.clone()))?; + } + } + + Ok(()) +} + +#[cfg(unittest)] +mod tests_signal { + use kerrno::KError; + use ksignal::SignalInfo; + use unittest::def_test; + + use super::{send_signal_to_process, send_signal_to_process_group, send_signal_to_thread}; + use crate::{ + cleanup_task_tables, get_process_group, get_process_state, get_session, get_task, processes, + tasks, + }; + + #[def_test] + fn test_cleanup_task_tables_on_empty_tables() { + cleanup_task_tables(); + } + + #[def_test] + fn test_task_tables_empty_queries_return_empty_or_not_found() { + cleanup_task_tables(); + + let _ = tasks(); + let _ = processes(); + assert!(matches!(get_task(12345), Err(KError::NoSuchProcess))); + assert!(matches!( + get_process_state(12345), + Err(KError::NoSuchProcess) + )); + assert!(matches!( + get_process_group(12345), + Err(KError::NoSuchProcess) + )); + assert!(matches!(get_session(12345), Err(KError::NoSuchProcess))); + } + + #[def_test] + fn test_send_signal_helpers_propagate_missing_target_errors() { + cleanup_task_tables(); + + assert!(matches!( + send_signal_to_thread( + None, + 22222, + Some(SignalInfo::new_kernel(ksignal::Signo::SIGTERM)) + ), + Err(KError::NoSuchProcess) + )); + assert!(matches!( + send_signal_to_process(22222, Some(SignalInfo::new_kernel(ksignal::Signo::SIGTERM))), + Err(KError::NoSuchProcess) + )); + assert!(matches!( + send_signal_to_process_group( + 22222, + Some(SignalInfo::new_kernel(ksignal::Signo::SIGTERM)) + ), + Err(KError::NoSuchProcess) + )); + } +} diff --git a/core/kcore/src/task/stat.rs b/process/kthread/src/stat.rs similarity index 81% rename from core/kcore/src/task/stat.rs rename to process/kthread/src/stat.rs index d49bdf76..45ab89a5 100644 --- a/core/kcore/src/task/stat.rs +++ b/process/kthread/src/stat.rs @@ -8,11 +8,9 @@ use kerrno::KResult; use ksignal::Signo; use ktask::{TaskInner, TaskState}; -use crate::task::AsThread; +use crate::AsThread; /// Represents the `/proc/[pid]/stat` file. -/// -/// See ['https://man7.org/linux/man-pages/man5/proc_pid_stat.5.html'] for details. #[derive(Default)] pub struct TaskStat { /// Process ID. @@ -61,7 +59,7 @@ pub struct TaskStat { pub starttime: u64, /// Virtual memory size in bytes. pub vsize: u64, - /// Resident Set Size. + /// Resident set size. pub rss: i64, /// Soft limit for RSS. pub rsslim: u64, @@ -103,30 +101,30 @@ pub struct TaskStat { pub guest_time: u64, /// Guest time of children. pub cguest_time: u64, - /// Address above which program initialized and uninitialized data are placed. + /// Address above which initialized and uninitialized data are placed. pub start_data: u64, - /// Address below which program initialized and uninitialized data are placed. + /// Address below which initialized and uninitialized data are placed. pub end_data: u64, - /// Address above which program heap can be expanded with brk. + /// Address above which heap can be expanded with `brk`. pub start_brk: u64, - /// Address above which program command-line arguments are placed. + /// Address above which command-line arguments are placed. pub arg_start: u64, - /// Address below which program command-line arguments are placed. + /// Address below which command-line arguments are placed. pub arg_end: u64, - /// Address above which program environment is placed. + /// Address above which environment is placed. pub env_start: u64, - /// Address below which program environment is placed. + /// Address below which environment is placed. pub env_end: u64, /// The thread's exit status. pub exit_code: i32, } impl TaskStat { - /// Create a new [`TaskStat`] from a [`KtaskRef`]. + /// Creates a new [`TaskStat`] from a [`TaskInner`]. pub fn from_thread(task: &TaskInner) -> KResult { let thread = task.as_thread(); - let proc_data = &thread.proc_data; - let proc = &proc_data.proc; + let proc_state = &thread.proc_state; + let proc = &proc_state.proc; let pid = proc.pid(); let comm = task.name(); @@ -147,7 +145,7 @@ impl TaskStat { pgrp, session, num_threads: proc.threads().len() as u32, - exit_signal: proc_data.exit_signal.unwrap_or(Signo::SIGCHLD) as u8, + exit_signal: proc_state.exit_signal().unwrap_or(Signo::SIGCHLD) as u8, exit_code: proc.exit_code(), ..Default::default() }) @@ -222,3 +220,31 @@ impl fmt::Display for TaskStat { ) } } + +#[cfg(unittest)] +mod tests_stat { + use unittest::def_test; + + use super::TaskStat; + + #[def_test] + fn test_taskstat_display_default() { + let stat = TaskStat::default(); + let text = alloc::format!("{stat}"); + let text = text.trim_end(); + assert!(text.starts_with("0 (")); + assert!(text.ends_with(" 0")); + } + + #[def_test] + fn test_taskstat_display_custom() { + let stat = TaskStat { + pid: 7, + comm: "init".into(), + state: 'R', + ..Default::default() + }; + let text = alloc::format!("{stat}"); + assert!(text.starts_with("7 (init) R ")); + } +} diff --git a/process/kthread/src/thread/core.rs b/process/kthread/src/thread/core.rs new file mode 100644 index 00000000..1fa5d4c5 --- /dev/null +++ b/process/kthread/src/thread/core.rs @@ -0,0 +1,175 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2025 KylinSoft Co., Ltd. +// See LICENSES for license details. + +use alloc::{boxed::Box, sync::Arc}; +use core::{ + cell::RefCell, + ops::Deref, + sync::atomic::{AtomicBool, AtomicI32, AtomicUsize, Ordering}, +}; + +use kprocess::Pid; +use ksignal::api::ThreadSignalManager; +use ktask::KtaskRef; +use ktimer::TimeManager; +#[cfg(feature = "tee")] +use ksync::Mutex; +#[cfg(feature = "tee")] +use tee_task_iface::TeeSessionCtxTrait; + +use crate::ProcessState; + +/// A wrapper type that assumes the inner type is `Sync`. +#[repr(transparent)] +pub struct AssumeSync(pub T); + +// SAFETY: `AssumeSync` wraps `RefCell`, which is only ever accessed from the +// owning thread (single-threaded access). No concurrent mutation occurs, so marking it +// `Sync` is sound as long as all access remains single-threaded. +unsafe impl Sync for AssumeSync {} + +impl Deref for AssumeSync { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +/// The current user thread handle. +pub struct CurrentThread(pub(super) KtaskRef); + +/// The inner data of a thread. +pub struct Thread { + /// The process state shared by all threads in the process. + pub proc_state: Arc, + + /// The clear thread tid field. + clear_child_tid: AtomicUsize, + + /// The head of the robust list. + robust_list_head: AtomicUsize, + + /// The thread-level signal manager. + pub signal: Arc, + + /// The time manager. + pub time: AssumeSync>, + + /// The OOM score adjustment value. + oom_score_adj: AtomicI32, + + /// Indicates whether the thread is ready to exit. + exit: AtomicBool, + + /// Indicates whether the thread is currently accessing user memory. + accessing_user_memory: AtomicBool, + + /// The TEE session context. + #[cfg(feature = "tee")] + pub tee_session_ctx: Mutex>>, +} + +impl Thread { + /// Creates a new [`Thread`]. + pub fn new(tid: u32, proc_state: Arc) -> Box { + Box::new(Thread { + signal: ThreadSignalManager::new(tid, proc_state.signal.clone()), + proc_state, + clear_child_tid: AtomicUsize::new(0), + robust_list_head: AtomicUsize::new(0), + time: AssumeSync(RefCell::new(TimeManager::new())), + oom_score_adj: AtomicI32::new(200), + exit: AtomicBool::new(false), + accessing_user_memory: AtomicBool::new(false), + #[cfg(feature = "tee")] + tee_session_ctx: Mutex::new(None), + }) + } + + /// Returns the clear-child-tid field. + pub fn clear_child_tid(&self) -> usize { + self.clear_child_tid.load(Ordering::Relaxed) + } + + /// Sets the clear-child-tid field. + pub fn set_clear_child_tid(&self, clear_child_tid: usize) { + self.clear_child_tid + .store(clear_child_tid, Ordering::Relaxed); + } + + /// Returns the robust-list head. + pub fn robust_list_head(&self) -> usize { + self.robust_list_head.load(Ordering::SeqCst) + } + + /// Sets the robust-list head. + pub fn set_robust_list_head(&self, robust_list_head: usize) { + self.robust_list_head + .store(robust_list_head, Ordering::SeqCst); + } + + /// Returns the OOM score adjustment value. + pub fn oom_score_adj(&self) -> i32 { + self.oom_score_adj.load(Ordering::SeqCst) + } + + /// Sets the OOM score adjustment value. + pub fn set_oom_score_adj(&self, value: i32) { + self.oom_score_adj.store(value, Ordering::SeqCst); + } + + /// Returns whether the thread is exiting. + pub fn is_exiting(&self) -> bool { + self.exit.load(Ordering::Acquire) + } + + /// Marks the thread as exiting. + pub fn set_exit(&self) { + self.exit.store(true, Ordering::Release); + } + + /// Returns whether the thread is currently accessing user memory. + pub fn is_accessing_user_memory(&self) -> bool { + self.accessing_user_memory.load(Ordering::Acquire) + } + + /// Sets the accessing-user-memory flag. + pub fn set_accessing_user_memory(&self, accessing: bool) { + self.accessing_user_memory + .store(accessing, Ordering::Release); + } + + /// Sets the TEE session context. + #[cfg(feature = "tee")] + pub fn set_tee_session_ctx(&self, ctx: Box) { + let mut guard = self.tee_session_ctx.lock(); + if guard.is_none() { + *guard = Some(ctx); + } + } + + /// Returns the shared process state for this thread. + pub fn process_state(&self) -> &Arc { + &self.proc_state + } + + /// Returns the process ID of this thread's process. + pub fn pid(&self) -> Pid { + self.process_state().proc.pid() + } +} + +#[cfg(unittest)] +mod tests_thread { + use unittest::def_test; + + use super::AssumeSync; + + #[def_test] + fn test_assume_sync_deref() { + let value = AssumeSync(42_u32); + assert_eq!(*value, 42); + } +} diff --git a/process/kthread/src/thread/current.rs b/process/kthread/src/thread/current.rs new file mode 100644 index 00000000..96d66535 --- /dev/null +++ b/process/kthread/src/thread/current.rs @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2025 KylinSoft Co., Ltd. +// See LICENSES for license details. + +use alloc::sync::Arc; +use core::ops::Deref; + +use ktask::current; + +use super::{AsThread, CurrentThread, Thread}; +use crate::ProcessState; + +impl Deref for CurrentThread { + type Target = Thread; + + fn deref(&self) -> &Self::Target { + self.0.as_thread() + } +} + +/// Returns the current user thread. +pub fn current_thread() -> CurrentThread { + CurrentThread(current().clone()) +} + +/// Executes a closure with the current user thread. +pub fn with_current_thread(f: impl FnOnce(&Thread) -> R) -> R { + let thread = current_thread(); + f(&thread) +} + +/// Returns the current process state. +pub fn current_process_state() -> Arc { + with_current_thread(|thread| thread.process_state().clone()) +} diff --git a/process/kthread/src/thread/mod.rs b/process/kthread/src/thread/mod.rs new file mode 100644 index 00000000..c4466b24 --- /dev/null +++ b/process/kthread/src/thread/mod.rs @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2025 KylinSoft Co., Ltd. +// See LICENSES for license details. + +mod core; +mod current; +mod task_ext; + +pub use core::{AssumeSync, CurrentThread, Thread}; +pub use current::{current_process_state, current_thread, with_current_thread}; +pub use task_ext::AsThread; diff --git a/process/kthread/src/thread/task_ext.rs b/process/kthread/src/thread/task_ext.rs new file mode 100644 index 00000000..ea735498 --- /dev/null +++ b/process/kthread/src/thread/task_ext.rs @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2025 KylinSoft Co., Ltd. +// See LICENSES for license details. + +use alloc::boxed::Box; + +use extern_trait::extern_trait; +use ktask::{TaskExt, TaskInner}; +use scope_local::ActiveScope; + +use super::Thread; + +/// Helper trait to access the thread from a task. +pub trait AsThread { + /// Tries to get the thread from the task. + fn try_as_thread(&self) -> Option<&Thread>; + + /// Returns the thread from the task, panicking if it is a kernel task. + fn as_thread(&self) -> &Thread { + self.try_as_thread().expect("kernel task") + } +} + +// SAFETY: `Box` is `Send` (thread state is not shared across threads during +// migration) and the `on_enter`/`on_leave` hooks correctly manage scope lifetime. +#[extern_trait] +unsafe impl TaskExt for Box { + fn on_enter(&self) { + let scope = self.proc_state.scope().read(); + // SAFETY: `scope` is a valid read guard obtained from the current thread's + // process state. It remains active and valid for the duration of this thread's + // execution until `on_leave` is called. + unsafe { ActiveScope::set(&scope) }; + // DESIGN: The read guard is intentionally leaked via `forget` so that the + // scope remains active for the entire lifetime of the thread. It is paired + // with `force_unlock_read()` in `on_leave`, which releases the guard without + // running its `Drop` implementation. + core::mem::forget(scope); + } + + fn on_leave(&self) { + ActiveScope::set_global(); + // SAFETY: `ActiveScope::set_global()` (above) has already unset the active + // scope for this thread, so no code path can access the read guard through + // the scope-local mechanism anymore. It is safe to force-unlock the read + // guard that was leaked in `on_enter`. + unsafe { self.proc_state.scope().force_unlock_read() }; + } +} + +impl AsThread for TaskInner { + fn try_as_thread(&self) -> Option<&Thread> { + self.task_ext() + .map(|ext| { + // SAFETY: The extension slot was populated with `Box` during + // thread creation (see `Thread::new`), so the concrete type is guaranteed + // to match the `downcast_ref` call. + unsafe { ext.downcast_ref::>() }.as_ref() + }) + } +} diff --git a/process/kthread/src/timer.rs b/process/kthread/src/timer.rs new file mode 100644 index 00000000..7217f413 --- /dev/null +++ b/process/kthread/src/timer.rs @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2025 KylinSoft Co., Ltd. +// See LICENSES for license details. + +//! Thread/process timer runtime surface. +//! +//! Timer runtime APIs are grouped here so per-thread CPU accounting and +//! interval-timer management have a dedicated process-side boundary. + +pub use ktimer::{TimeManager, TimerState}; +use ktypes::Once; + +static TIMER_RUNTIME_INIT: Once<()> = Once::new(); + +/// Installs the timer-expiration bridge and spawns the alarm task once. +pub fn spawn_alarm_task() { + TIMER_RUNTIME_INIT.call_once(|| { + ktimer::register_expired_task_handler(crate::poll_timer); + ktimer::spawn_alarm_task(); + }); +} diff --git a/process/ktimer/Cargo.toml b/process/ktimer/Cargo.toml new file mode 100644 index 00000000..744de43b --- /dev/null +++ b/process/ktimer/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "ktimer" +description = "Process-side timer runtime for X-Kernel" +version.workspace = true +edition.workspace = true +authors.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +documentation.workspace = true + +[dependencies] +event-listener = { workspace = true, default-features = false } +kbuild_config.workspace = true +khal.workspace = true +ksignal.workspace = true +ksync.workspace = true +ktask.workspace = true +ktypes.workspace = true +lazy_static = { workspace = true } +posix-types.workspace = true +unittest.workspace = true + diff --git a/core/kcore/src/time.rs b/process/ktimer/src/lib.rs similarity index 76% rename from core/kcore/src/time.rs rename to process/ktimer/src/lib.rs index 923b634c..5e24a558 100644 --- a/core/kcore/src/time.rs +++ b/process/ktimer/src/lib.rs @@ -2,7 +2,17 @@ // Copyright 2025 KylinSoft Co., Ltd. // See LICENSES for license details. -//! Time management module. +//! Process-side timer runtime. + +#![no_std] + +macro_rules! __log_prefix { + () => { + "ktimer: " + }; +} + +extern crate alloc; use alloc::{borrow::ToOwned, collections::binary_heap::BinaryHeap, sync::Arc}; use core::{mem, time::Duration}; @@ -12,13 +22,12 @@ use khal::time::{NANOS_PER_SEC, TimeValue, monotonic_time, monotonic_time_nanos} use ksignal::Signo; use ksync::Mutex; use ktask::{ - WeakKtaskRef, current, + TaskInner, WeakKtaskRef, current, future::{block_on, timeout_at}, }; +use ktypes::Once; use lazy_static::lazy_static; -use strum::FromRepr; - -use crate::task::poll_timer; +use posix_types::ITimerType; fn time_value_from_nanos(nanos: usize) -> TimeValue { let secs = nanos as u64 / NANOS_PER_SEC; @@ -30,17 +39,21 @@ struct Entry { deadline: Duration, task: WeakKtaskRef, } + impl PartialEq for Entry { fn eq(&self, other: &Self) -> bool { self.deadline == other.deadline } } + impl Eq for Entry {} + impl PartialOrd for Entry { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } + impl Ord for Entry { fn cmp(&self, other: &Self) -> core::cmp::Ordering { other.deadline.cmp(&self.deadline) @@ -49,66 +62,52 @@ impl Ord for Entry { lazy_static! { static ref ALARM_LIST: Mutex> = Mutex::new(BinaryHeap::new()); - static ref EVENT_NEW_TIMER: Event = Event::new(); } -/// The type of interval timer. -#[repr(i32)] -#[allow(non_camel_case_types)] -#[derive(Eq, PartialEq, Debug, Clone, Copy, FromRepr)] -pub enum ITimerType { - /// 统计系统实际运行时间 - Real = 0, - /// 统计用户态运行时间 - Virtual = 1, - /// 统计进程的所有用户态/内核态运行时间 - Prof = 2, -} +static EVENT_NEW_TIMER: Event = Event::new(); +static EXPIRED_TASK_HANDLER: Once = Once::new(); -impl ITimerType { - /// Returns the signal number associated with this timer type. - pub fn signo(&self) -> Signo { - match self { - ITimerType::Real => Signo::SIGALRM, - ITimerType::Virtual => Signo::SIGVTALRM, - ITimerType::Prof => Signo::SIGPROF, - } +fn timer_signal(ty: ITimerType) -> Signo { + match ty { + ITimerType::Real => Signo::SIGALRM, + ITimerType::Virtual => Signo::SIGVTALRM, + ITimerType::Prof => Signo::SIGPROF, } } #[derive(Default)] struct ITimer { interval_ns: usize, - remained_ns: usize, + remaining_ns: usize, } impl ITimer { - pub fn new(interval_ns: usize, remained_ns: usize) -> Self { + fn new(interval_ns: usize, remaining_ns: usize) -> Self { let result = Self { interval_ns, - remained_ns, + remaining_ns, }; result.renew_timer(); result } - pub fn update(&mut self, delta: usize) -> bool { - if self.remained_ns == 0 { + fn update(&mut self, delta: usize) -> bool { + if self.remaining_ns == 0 { return false; } - if self.remained_ns > delta { - self.remained_ns -= delta; + if self.remaining_ns > delta { + self.remaining_ns -= delta; false } else { - self.remained_ns = self.interval_ns; + self.remaining_ns = self.interval_ns; self.renew_timer(); true } } - pub fn renew_timer(&self) { - if self.remained_ns > 0 { - let deadline = monotonic_time() + Duration::from_nanos(self.remained_ns as u64); + fn renew_timer(&self) { + if self.remaining_ns > 0 { + let deadline = monotonic_time() + Duration::from_nanos(self.remaining_ns as u64); let mut guard = ALARM_LIST.lock(); let should_wake = guard.peek().is_none_or(|it| it.deadline > deadline); guard.push(Entry { @@ -134,8 +133,7 @@ pub enum TimerState { Kernel, } -// TODO(mivik): preempting does not change the timer state currently -/// A manager for time-related operations. +/// A manager for per-thread timer and CPU-time accounting. pub struct TimeManager { utime_ns: usize, stime_ns: usize, @@ -151,7 +149,8 @@ impl Default for TimeManager { } impl TimeManager { - pub(crate) fn new() -> Self { + /// Creates a new [`TimeManager`]. + pub fn new() -> Self { Self { utime_ns: 0, stime_ns: 0, @@ -168,8 +167,7 @@ impl TimeManager { (utime, stime) } - /// Polls the time manager to update the timers and emit signals if - /// necessary. + /// Polls the time manager to update timers and emit signals if necessary. pub fn poll(&mut self, emitter: impl Fn(Signo)) { let now_ns = monotonic_time_nanos() as usize; let delta = now_ns - self.last_wall_ns; @@ -200,15 +198,15 @@ impl TimeManager { &mut self, ty: ITimerType, interval_ns: usize, - remained_ns: usize, + remaining_ns: usize, ) -> (TimeValue, TimeValue) { let old = mem::replace( &mut self.itimers[ty as usize], - ITimer::new(interval_ns, remained_ns), + ITimer::new(interval_ns, remaining_ns), ); ( time_value_from_nanos(old.interval_ns), - time_value_from_nanos(old.remained_ns), + time_value_from_nanos(old.remaining_ns), ) } @@ -217,17 +215,22 @@ impl TimeManager { let itimer = &self.itimers[ty as usize]; ( time_value_from_nanos(itimer.interval_ns), - time_value_from_nanos(itimer.remained_ns), + time_value_from_nanos(itimer.remaining_ns), ) } fn update_itimer(&mut self, ty: ITimerType, delta: usize, emitter: impl Fn(Signo)) { if self.itimers[ty as usize].update(delta) { - emitter(ty.signo()); + emitter(timer_signal(ty)); } } } +/// Registers the callback used to handle expired timer tasks. +pub fn register_expired_task_handler(handler: fn(&TaskInner)) { + EXPIRED_TASK_HANDLER.call_once(|| handler); +} + async fn alarm_task() { loop { let entry = { @@ -246,19 +249,16 @@ async fn alarm_task() { let now = monotonic_time(); if deadline <= now { - // 任务已到期,执行它 - if let Some(task) = task_weak.upgrade() { - poll_timer(&task); + if let Some(task) = task_weak.upgrade() + && let Some(handler) = EXPIRED_TASK_HANDLER.get() + { + handler(&task); } - // 从队列中移除 let mut guard = ALARM_LIST.lock(); assert!(guard.pop().is_some_and(|it| it.deadline == deadline)); } else { - // 任务未到期,等待到 deadline 或新任务插入 listener!(EVENT_NEW_TIMER => listener); - - // 检查队列头是否还是同一个任务 if ALARM_LIST .lock() .peek() @@ -281,35 +281,25 @@ pub fn spawn_alarm_task() { ); } -/// Unit tests. #[cfg(unittest)] -pub mod tests_time { +mod tests { use alloc::vec::Vec; use core::cell::RefCell; use ksignal::Signo; + use posix_types::ITimerType; use unittest::def_test; - use super::{ITimerType, TimeManager}; - - #[def_test] - fn test_itimer_signo() { - assert_eq!(ITimerType::Real.signo(), Signo::SIGALRM); - assert_eq!(ITimerType::Virtual.signo(), Signo::SIGVTALRM); - assert_eq!(ITimerType::Prof.signo(), Signo::SIGPROF); - } - #[def_test] - fn test_itimer_from_repr() { - assert_eq!(ITimerType::from_repr(0), Some(ITimerType::Real)); - assert_eq!(ITimerType::from_repr(1), Some(ITimerType::Virtual)); - assert_eq!(ITimerType::from_repr(2), Some(ITimerType::Prof)); - assert_eq!(ITimerType::from_repr(3), None); + fn test_timer_signal_mapping() { + assert_eq!(super::timer_signal(ITimerType::Real), Signo::SIGALRM); + assert_eq!(super::timer_signal(ITimerType::Virtual), Signo::SIGVTALRM); + assert_eq!(super::timer_signal(ITimerType::Prof), Signo::SIGPROF); } #[def_test] fn test_timemanager_default_output() { - let tm = TimeManager::new(); + let tm = super::TimeManager::new(); let (u, s) = tm.output(); assert_eq!(u.as_secs(), 0); assert_eq!(u.subsec_nanos(), 0); @@ -340,7 +330,7 @@ pub mod tests_time { #[def_test] fn test_timemanager_get_itimer_default() { - let tm = TimeManager::new(); + let tm = super::TimeManager::new(); for ty in [ITimerType::Real, ITimerType::Virtual, ITimerType::Prof] { let (interval, remained) = tm.get_itimer(ty); assert_eq!(interval.as_secs(), 0); @@ -350,7 +340,7 @@ pub mod tests_time { #[def_test] fn test_timemanager_set_state() { - let mut tm = TimeManager::new(); + let mut tm = super::TimeManager::new(); tm.set_state(super::TimerState::User); tm.set_state(super::TimerState::Kernel); tm.set_state(super::TimerState::None); @@ -369,25 +359,25 @@ pub mod tests_time { fn test_itimer_update_counts_down_without_firing() { let mut timer = super::ITimer { interval_ns: 0, - remained_ns: 10, + remaining_ns: 10, }; assert!(!timer.update(3)); - assert_eq!(timer.remained_ns, 7); + assert_eq!(timer.remaining_ns, 7); } #[def_test] fn test_itimer_update_fires_and_resets_to_interval() { let mut timer = super::ITimer { interval_ns: 0, - remained_ns: 5, + remaining_ns: 5, }; assert!(timer.update(5)); - assert_eq!(timer.remained_ns, 0); + assert_eq!(timer.remaining_ns, 0); } #[def_test] fn test_timemanager_set_itimer_returns_previous_values() { - let mut tm = TimeManager::new(); + let mut tm = super::TimeManager::new(); let (old_interval, old_remained) = tm.set_itimer(ITimerType::Real, 11, 22); assert_eq!(old_interval.as_secs(), 0); @@ -404,10 +394,10 @@ pub mod tests_time { #[def_test] fn test_timemanager_update_itimer_emits_signal_on_expiration() { - let mut tm = TimeManager::new(); + let mut tm = super::TimeManager::new(); tm.itimers[ITimerType::Real as usize] = super::ITimer { interval_ns: 0, - remained_ns: 1, + remaining_ns: 1, }; let emitted = RefCell::new(Vec::new()); @@ -422,10 +412,10 @@ pub mod tests_time { #[def_test] fn test_timemanager_update_itimer_no_emit_before_expiration() { - let mut tm = TimeManager::new(); + let mut tm = super::TimeManager::new(); tm.itimers[ITimerType::Prof as usize] = super::ITimer { interval_ns: 0, - remained_ns: 10, + remaining_ns: 10, }; let emitted = RefCell::new(Vec::new()); diff --git a/tee/tee_kernel/Cargo.toml b/tee/tee_kernel/Cargo.toml index 3c037a03..41aa9d3e 100644 --- a/tee/tee_kernel/Cargo.toml +++ b/tee/tee_kernel/Cargo.toml @@ -43,6 +43,7 @@ cfg-if.workspace = true lazy_static = { workspace = true } linux-raw-sys = { workspace = true, features = ["ioctl", "loop_device"] } osvm.workspace = true +kthread = { workspace = true, features = ["tee"] } linux_sysno.workspace = true zerocopy = { version = "0.8.26", features = ["derive"] } bincode = { version = "2.0.1", default-features = false, features = ["alloc", "derive"] } @@ -51,7 +52,6 @@ tee_task_iface = { workspace = true } uuid = { version = "0.8", default-features = false } hex = { version = "0.4", default-features = false, features = ["alloc"] } cty = "0.2" -kcore.workspace = true kbuild_config.workspace = true subtle = { version = "2", default-features = false } static_assertions = "1.1.0" diff --git a/tee/tee_kernel/src/tee/common/file_ops.rs b/tee/tee_kernel/src/tee/common/file_ops.rs index 7e5c0308..0020673b 100644 --- a/tee/tee_kernel/src/tee/common/file_ops.rs +++ b/tee/tee_kernel/src/tee/common/file_ops.rs @@ -6,12 +6,11 @@ use alloc::{format, sync::Arc, vec::Vec}; use core::ffi::c_int; use fs_ng_vfs::{NodePermission, VfsError}; -use kcore::task::AsThread; use kerrno::{KError, KResult}; use kfs::{FS_CONTEXT, File, FileBackend, FileFlags, OpenOptions, OpenResult}; use kio::{Seek, SeekFrom}; use ksync::RwLock; -use ktask::current; +use kthread; use linux_raw_sys::general::*; use scope_local::scope_local; use slab::Slab; @@ -186,7 +185,7 @@ impl FileVariant { flags, mode ); - let mode = mode & !current().as_thread().proc_data.umask(); + let mode = mode & !kthread::current_process_state().umask(); let options = flags_to_options(flags as c_int, mode as __kernel_mode_t, (0, 0)); let fd = with_fs(AT_FDCWD, |fs| options.open(fs, path)) diff --git a/tee/tee_kernel/src/tee/tee_generic.rs b/tee/tee_kernel/src/tee/tee_generic.rs index 9142ccab..b3eaf69c 100644 --- a/tee/tee_kernel/src/tee/tee_generic.rs +++ b/tee/tee_kernel/src/tee/tee_generic.rs @@ -6,12 +6,11 @@ use alloc::{format, vec::Vec}; use core::ffi::c_char; use bincode::config; -use kcore::task::AsThread; use knet::{ SendOptions, SocketAddrEx, SocketOps, unix::{StreamTransport, UnixAddr, UnixDomainSocket}, }; -use ktask::current; +use kthread; use tee_raw_sys::{TEE_ERROR_BAD_PARAMETERS, TEE_ERROR_GENERIC}; use crate::{ @@ -42,9 +41,7 @@ pub fn sys_tee_scn_log(buf: *const c_char, len: usize) -> TeeResult { /// Panic the current TEE application pub fn sys_tee_scn_panic(panic_code: u32) -> TeeResult { // Connect to current TA via Unix socket - let socket = UnixDomainSocket::new(StreamTransport::new( - current().as_thread().proc_data.proc.pid(), - )); + let socket = UnixDomainSocket::new(StreamTransport::new(kthread::current_thread().pid())); let uuid = with_tee_ta_ctx(|ctx| Ok(ctx.uuid.clone()))?; let path = format!("/tmp/{}.sock", uuid); let remote_addr = SocketAddrEx::Unix(UnixAddr::Path(path.into())); diff --git a/tee/tee_kernel/src/tee/tee_obj.rs b/tee/tee_kernel/src/tee/tee_obj.rs index c4473c26..5bdf1e9a 100644 --- a/tee/tee_kernel/src/tee/tee_obj.rs +++ b/tee/tee_kernel/src/tee/tee_obj.rs @@ -11,10 +11,10 @@ use core::{default, ffi::c_ulong, fmt, fmt::Debug}; use bincode::de; use flatten_objects::FlattenObjects; -use kcore::task::{AsThread, TeeSessionCtxTrait}; use kerrno::{KError, KResult}; use ksync::{Mutex, RwLock}; use ktask::current; +use kthread::{AsThread, TeeSessionCtxTrait}; use slab::Slab; use tee_raw_sys::{libc_compat::size_t, *}; diff --git a/tee/tee_kernel/src/tee/tee_session.rs b/tee/tee_kernel/src/tee/tee_session.rs index 62888c76..e77d240c 100644 --- a/tee/tee_kernel/src/tee/tee_session.rs +++ b/tee/tee_kernel/src/tee/tee_session.rs @@ -9,9 +9,8 @@ use alloc::{ }; use core::{any::Any, default::Default}; -use kcore::task::{AsThread, TeeSessionCtxTrait}; use ksync::{Mutex, RwLock}; -use ktask::current; +use kthread::{self, TeeSessionCtxTrait, Thread}; use slab::Slab; use tee_raw_sys::*; use tee_task_iface::TeeTaCtx; @@ -90,23 +89,20 @@ pub fn with_tee_session_ctx_mut(f: F) -> TeeResult where F: FnOnce(&mut TeeSessionCtx) -> TeeResult, { - let current_task = current(); - current_task - .as_thread() - .set_tee_session_ctx(Box::new(TeeSessionCtx::default())); - - let binding = ¤t_task.as_thread().tee_session_ctx; - let mut lock = binding.lock(); - - let concrete = { - let boxed = lock.as_mut().ok_or(TEE_ERROR_BAD_STATE)?; - boxed - .as_any_mut() - .downcast_mut::() - .ok_or(TEE_ERROR_BAD_STATE)? - }; - - f(concrete) + kthread::with_current_thread(|thread| { + thread.set_tee_session_ctx(Box::new(TeeSessionCtx::default())); + + let mut lock = thread.tee_session_ctx.lock(); + let concrete = { + let boxed = lock.as_mut().ok_or(TEE_ERROR_BAD_STATE)?; + boxed + .as_any_mut() + .downcast_mut::() + .ok_or(TEE_ERROR_BAD_STATE)? + }; + + f(concrete) + }) } /// Acquire an immutable reference to the current thread's tee_session_ctx @@ -124,27 +120,24 @@ pub fn with_tee_session_ctx(f: F) -> TeeResult where F: FnOnce(&TeeSessionCtx) -> TeeResult, { - let current_task = current(); - current_task - .as_thread() - .set_tee_session_ctx(Box::new(TeeSessionCtx::default())); - - let binding = ¤t_task.as_thread().tee_session_ctx; - let lock = binding.lock(); - - let concrete = { - let boxed = lock.as_ref().ok_or(TEE_ERROR_BAD_STATE)?; - boxed - .as_any() - .downcast_ref::() - .ok_or(TEE_ERROR_BAD_STATE)? - }; - - f(concrete) + kthread::with_current_thread(|thread| { + thread.set_tee_session_ctx(Box::new(TeeSessionCtx::default())); + + let lock = thread.tee_session_ctx.lock(); + let concrete = { + let boxed = lock.as_ref().ok_or(TEE_ERROR_BAD_STATE)?; + boxed + .as_any() + .downcast_ref::() + .ok_or(TEE_ERROR_BAD_STATE)? + }; + + f(concrete) + }) } #[cfg(unittest)] -pub fn set_tee_session_ctx(thread: &kcore::task::Thread) { +pub fn set_tee_session_ctx(thread: &Thread) { thread.set_tee_session_ctx(Box::new(TeeSessionCtx::default())); } @@ -161,8 +154,8 @@ pub fn with_tee_ta_ctx_mut(f: F) -> TeeResult where F: FnOnce(&mut TeeTaCtx) -> TeeResult, { - let current_task = current(); - let mut lock = current_task.as_thread().proc_data.tee_ta_ctx.write(); + let proc_state = kthread::current_process_state(); + let mut lock = proc_state.tee_ta_ctx.write(); f(&mut lock) } @@ -179,8 +172,8 @@ pub fn with_tee_ta_ctx(f: F) -> TeeResult where F: FnOnce(&TeeTaCtx) -> TeeResult, { - let current_task = current(); - let lock = current_task.as_thread().proc_data.tee_ta_ctx.read(); + let proc_state = kthread::current_process_state(); + let lock = proc_state.tee_ta_ctx.read(); f(&lock) } diff --git a/tee/tee_kernel/src/tee/tee_ta_manager.rs b/tee/tee_kernel/src/tee/tee_ta_manager.rs index e976028c..4a6700d0 100644 --- a/tee/tee_kernel/src/tee/tee_ta_manager.rs +++ b/tee/tee_kernel/src/tee/tee_ta_manager.rs @@ -5,12 +5,11 @@ use alloc::{format, string::String, vec::Vec}; use bincode::config; -use kcore::task::AsThread; use knet::{ RecvOptions, SendOptions, SocketAddrEx, SocketOps, unix::{StreamTransport, UnixAddr, UnixDomainSocket}, }; -use ktask::current; +use kthread; use tee_raw_sys::{TEE_ERROR_GENERIC, TEE_ERROR_ITEM_NOT_FOUND, TEE_SUCCESS, utee_params}; use tee_task_iface::SessionIdentity; @@ -22,9 +21,7 @@ use crate::tee::{ pub fn tee_ta_init_session(uuid: String) -> TeeResult { // Connect to dest TA via Unix socket - let socket = UnixDomainSocket::new(StreamTransport::new( - current().as_thread().proc_data.proc.pid(), - )); + let socket = UnixDomainSocket::new(StreamTransport::new(kthread::current_thread().pid())); let path = format!("/tmp/{}.sock", uuid); let remote_addr = SocketAddrEx::Unix(UnixAddr::Path(path.into())); socket.connect(remote_addr).map_err(|_| TEE_ERROR_GENERIC)?; @@ -69,9 +66,7 @@ pub fn tee_ta_init_session(uuid: String) -> TeeResult { pub fn tee_ta_close_session(sess_id: SessionIdentity) -> TeeResult { // Connect to dest TA via Unix socket - let socket = UnixDomainSocket::new(StreamTransport::new( - current().as_thread().proc_data.proc.pid(), - )); + let socket = UnixDomainSocket::new(StreamTransport::new(kthread::current_thread().pid())); let path = format!("/tmp/{}.sock", sess_id.uuid); let remote_addr = SocketAddrEx::Unix(UnixAddr::Path(path.into())); socket.connect(remote_addr).map_err(|_| TEE_ERROR_GENERIC)?; @@ -98,9 +93,7 @@ pub fn tee_ta_invoke_command( _usr_param: *mut utee_params, ) -> TeeResult { // Connect to dest TA via Unix socket - let socket = UnixDomainSocket::new(StreamTransport::new( - current().as_thread().proc_data.proc.pid(), - )); + let socket = UnixDomainSocket::new(StreamTransport::new(kthread::current_thread().pid())); let path = format!("/tmp/{}.sock", sess_id.uuid); let remote_addr = SocketAddrEx::Unix(UnixAddr::Path(path.into())); socket.connect(remote_addr).map_err(|_| TEE_ERROR_GENERIC)?; diff --git a/util/unittest_support/Cargo.toml b/util/unittest_support/Cargo.toml index 47089e59..cfc5675a 100644 --- a/util/unittest_support/Cargo.toml +++ b/util/unittest_support/Cargo.toml @@ -14,8 +14,9 @@ kalloc.workspace = true kcore.workspace = true kerrno.workspace = true khal.workspace = true +kthread.workspace = true ksync.workspace = true ktask.workspace = true memaddr.workspace = true memspace.workspace = true -osvm.workspace = true \ No newline at end of file +osvm.workspace = true diff --git a/util/unittest_support/src/lib.rs b/util/unittest_support/src/lib.rs index bb31f085..220714fd 100644 --- a/util/unittest_support/src/lib.rs +++ b/util/unittest_support/src/lib.rs @@ -15,11 +15,11 @@ use core::{ sync::atomic::{AtomicUsize, Ordering}, }; -use kcore::task::AsThread; use kerrno::{KError, KResult}; use khal::{mem::v2p, paging::MappingFlags}; use ksync::Mutex; use ktask::current; +use kthread::AsThread; use memaddr::{PAGE_SIZE_4K, VirtAddr}; use osvm::{read_vm_mem, write_vm_mem}; @@ -50,7 +50,7 @@ impl TestUserBuffer { pub fn new(len: usize) -> KResult { let current_task = current(); let thread = current_task.try_as_thread().ok_or(KError::BadState)?; - let aspace = thread.proc_data.aspace.clone(); + let aspace = thread.proc_state.address_space().clone(); let mapped_size = len.max(1).next_multiple_of(PAGE_SIZE_4K); let num_pages = mapped_size / PAGE_SIZE_4K; let kernel_va = kalloc::global_allocator() -- Gitee From 8f28c0765471b213deaa8ff7505940284d0057a5 Mon Sep 17 00:00:00 2001 From: Weikang Guo Date: Thu, 7 May 2026 17:56:06 +0800 Subject: [PATCH 03/21] fix fmt Signed-off-by: Weikang Guo --- core/kservices/src/file/mod.rs | 2 +- core/kservices/src/file/pipe.rs | 4 +-- core/kservices/src/file/signalfd.rs | 4 +-- core/ksyscall/src/fs/signalfd.rs | 4 +-- core/ksyscall/src/io_mpx/epoll.rs | 8 ++--- core/ksyscall/src/task/clone.rs | 10 ++++-- core/ksyscall/src/task/mod.rs | 3 +- core/ksyscall/src/task/wait.rs | 4 +-- posix/fs/src/fd_ops.rs | 4 +-- posix/mm/src/mmap.rs | 18 ++++++++--- posix/process/src/lib.rs | 42 +++++++++++++++----------- posix/sched/src/lib.rs | 2 +- posix/signal/src/lib.rs | 10 +++--- posix/sync/src/lib.rs | 3 +- process/kresources/src/lib.rs | 5 ++- process/kthread/src/lib.rs | 8 ++--- process/kthread/src/posix_state.rs | 6 +--- process/kthread/src/process_state.rs | 2 +- process/kthread/src/signal.rs | 4 +-- process/kthread/src/thread/core.rs | 4 +-- process/kthread/src/thread/mod.rs | 1 + process/kthread/src/thread/task_ext.rs | 13 ++++---- 22 files changed, 84 insertions(+), 77 deletions(-) diff --git a/core/kservices/src/file/mod.rs b/core/kservices/src/file/mod.rs index 7a0039c2..72034577 100644 --- a/core/kservices/src/file/mod.rs +++ b/core/kservices/src/file/mod.rs @@ -16,11 +16,11 @@ pub mod timerfd; use alloc::sync::Arc; use kerrno::{KError, KResult}; +use kfd::{FdTable, FileDescriptor}; pub use kfd::{ FileLike, IoDst, IoSrc, Kstat, ReadBuf, WriteBuf, add_file_like, close_all_fds, close_file_like, current_fd_table, get_file_like, new_fd_table, }; -use kfd::{FdTable, FileDescriptor}; use kfs::{FS_CONTEXT, OpenOptions}; use linux_raw_sys::general::{O_RDONLY, O_WRONLY}; diff --git a/core/kservices/src/file/pipe.rs b/core/kservices/src/file/pipe.rs index a1fee328..ffa672d2 100644 --- a/core/kservices/src/file/pipe.rs +++ b/core/kservices/src/file/pipe.rs @@ -13,9 +13,7 @@ use kerrno::{KError, KResult}; use kpoll::{IoEvents, PollSet, Pollable}; use ksignal::{SignalInfo, Signo}; use ksync::Mutex; -use ktask::{ - future::{block_on, poll_io}, -}; +use ktask::future::{block_on, poll_io}; use kthread::send_signal_to_process; use linux_raw_sys::{ general::{O_RDONLY, O_WRONLY, S_IFIFO}, diff --git a/core/kservices/src/file/signalfd.rs b/core/kservices/src/file/signalfd.rs index 4dbfd895..7e9e6560 100644 --- a/core/kservices/src/file/signalfd.rs +++ b/core/kservices/src/file/signalfd.rs @@ -13,9 +13,7 @@ use kerrno::{KError, KResult}; use kpoll::{IoEvents, PollSet, Pollable}; use ksignal::{SignalInfo, SignalSet}; use ksync::RwLock; -use ktask::{ - future::{block_on, poll_io}, -}; +use ktask::future::{block_on, poll_io}; use zerocopy::{Immutable, IntoBytes}; use crate::file::{FileLike, IoDst, IoSrc}; diff --git a/core/ksyscall/src/fs/signalfd.rs b/core/ksyscall/src/fs/signalfd.rs index 4ad5db5c..c384980c 100644 --- a/core/ksyscall/src/fs/signalfd.rs +++ b/core/ksyscall/src/fs/signalfd.rs @@ -16,9 +16,7 @@ use linux_raw_sys::general::{O_CLOEXEC, O_NONBLOCK}; use osvm::VirtPtr; use posix_signal::check_sigset_size; -use crate::{ - file::{FileLike, add_file_like, signalfd::Signalfd}, -}; +use crate::file::{FileLike, add_file_like, signalfd::Signalfd}; // SFD flag definitions (if not available in linux_raw_sys) const SFD_CLOEXEC: u32 = O_CLOEXEC; diff --git a/core/ksyscall/src/io_mpx/epoll.rs b/core/ksyscall/src/io_mpx/epoll.rs index a2f8a1ea..f1ba3452 100644 --- a/core/ksyscall/src/io_mpx/epoll.rs +++ b/core/ksyscall/src/io_mpx/epoll.rs @@ -28,11 +28,9 @@ use linux_raw_sys::general::{ use posix_signal::check_sigset_size; use posix_types::TimeValueLike; -use crate::{ - file::{ - FileLike, - epoll::{Epoll, EpollEvent, EpollFlags}, - }, +use crate::file::{ + FileLike, + epoll::{Epoll, EpollEvent, EpollFlags}, }; bitflags! { diff --git a/core/ksyscall/src/task/clone.rs b/core/ksyscall/src/task/clone.rs index 3f605475..c949df2a 100644 --- a/core/ksyscall/src/task/clone.rs +++ b/core/ksyscall/src/task/clone.rs @@ -231,9 +231,13 @@ impl CloneRequest { } let new_proc_data = if self.flags.contains(CloneFlags::THREAD) { - new_task - .ctx_mut() - .set_page_table_root(old_proc_data.address_space().lock().page_table_root().into()); + new_task.ctx_mut().set_page_table_root( + old_proc_data + .address_space() + .lock() + .page_table_root() + .into(), + ); old_proc_data.clone() } else { let proc = if self.flags.contains(CloneFlags::PARENT) { diff --git a/core/ksyscall/src/task/mod.rs b/core/ksyscall/src/task/mod.rs index 4bf8127c..77821a10 100644 --- a/core/ksyscall/src/task/mod.rs +++ b/core/ksyscall/src/task/mod.rs @@ -22,7 +22,6 @@ mod wait; pub use posix_sched::*; -pub use self::{clone::*, clone3::*, ctl::*, execve::*, exit::*, wait::*}; - #[cfg(target_arch = "x86_64")] pub use self::thread::*; +pub use self::{clone::*, clone3::*, ctl::*, execve::*, exit::*, wait::*}; diff --git a/core/ksyscall/src/task/wait.rs b/core/ksyscall/src/task/wait.rs index fc215a1e..b90d4c8f 100644 --- a/core/ksyscall/src/task/wait.rs +++ b/core/ksyscall/src/task/wait.rs @@ -15,9 +15,7 @@ use core::{future::poll_fn, task::Poll}; use bitflags::bitflags; use kerrno::{KError, KResult, LinuxError}; use kprocess::{Pid, Process}; -use ktask::{ - future::{block_on, interruptible}, -}; +use ktask::future::{block_on, interruptible}; use linux_raw_sys::general::{ __WALL, __WCLONE, __WNOTHREAD, WCONTINUED, WEXITED, WNOHANG, WNOWAIT, WUNTRACED, }; diff --git a/posix/fs/src/fd_ops.rs b/posix/fs/src/fd_ops.rs index 094cd88e..436aec6f 100644 --- a/posix/fs/src/fd_ops.rs +++ b/posix/fs/src/fd_ops.rs @@ -9,9 +9,7 @@ //! - File descriptor duplication (dup, dup2, dup3, etc.) //! - File descriptor flags and control (fcntl, etc.) -use core::{ - ffi::c_int, -}; +use core::ffi::c_int; use bitflags::bitflags; use kerrno::{KError, KResult}; diff --git a/posix/mm/src/mmap.rs b/posix/mm/src/mmap.rs index 6ca63191..8d52ef7c 100644 --- a/posix/mm/src/mmap.rs +++ b/posix/mm/src/mmap.rs @@ -189,7 +189,13 @@ pub fn sys_mmap( match file.backend()?.clone() { FileBackend::Cached(cache) => { // TODO(mivik): file mmap page size - new_file(start, cache, file.flags(), offset, proc_state.address_space()) + new_file( + start, + cache, + file.flags(), + offset, + proc_state.address_space(), + ) } FileBackend::Direct(loc) => { if let Ok(device) = loc.entry().downcast::() { @@ -210,9 +216,13 @@ pub fn sys_mmap( start.as_usize() as isize - range.start.as_usize() as isize, ) } - DeviceMmap::Cache(cache) => { - new_file(start, cache, file.flags(), offset, proc_state.address_space()) - } + DeviceMmap::Cache(cache) => new_file( + start, + cache, + file.flags(), + offset, + proc_state.address_space(), + ), } } else { // Preserve MAP_SHARED semantics for direct-opened regular files. diff --git a/posix/process/src/lib.rs b/posix/process/src/lib.rs index 2da9acb0..9fae0df1 100644 --- a/posix/process/src/lib.rs +++ b/posix/process/src/lib.rs @@ -17,8 +17,8 @@ macro_rules! __log_prefix { }; } -use khal::time::TimeValue; use kerrno::{KError, KResult}; +use khal::time::TimeValue; use kprocess::{Pid, Process}; use ktask::current; use kthread::{AsThread, Thread, get_process_group, get_process_state, get_task}; @@ -94,7 +94,9 @@ pub fn sys_setpgid(pid: Pid, pgid: Pid) -> KResult { /// Sets the process umask and returns the previous value. pub fn sys_umask(mask: u32) -> KResult { - let old = kthread::current_thread().process_state().replace_umask(mask); + let old = kthread::current_thread() + .process_state() + .replace_umask(mask); Ok(old as isize) } @@ -129,26 +131,30 @@ impl From for rusage { } fn self_rusage(proc: &Process) -> Rusage { - proc.threads().into_iter().fold(Rusage::default(), |acc, tid| { - if let Ok(task) = get_task(tid) { - acc.collate(Rusage::from_thread(task.as_thread())) - } else { - acc - } - }) + proc.threads() + .into_iter() + .fold(Rusage::default(), |acc, tid| { + if let Ok(task) = get_task(tid) { + acc.collate(Rusage::from_thread(task.as_thread())) + } else { + acc + } + }) } fn children_rusage(proc: &Process) -> Rusage { let current = current(); - proc.threads().into_iter().fold(Rusage::default(), |acc, child| { - if let Ok(task) = get_task(child) - && !current.ptr_eq(&task) - { - acc.collate(Rusage::from_thread(task.as_thread())) - } else { - acc - } - }) + proc.threads() + .into_iter() + .fold(Rusage::default(), |acc, child| { + if let Ok(task) = get_task(child) + && !current.ptr_eq(&task) + { + acc.collate(Rusage::from_thread(task.as_thread())) + } else { + acc + } + }) } /// Returns resource usage information for the current process, its children, or the current thread. diff --git a/posix/sched/src/lib.rs b/posix/sched/src/lib.rs index 96051e8c..884bbfd1 100644 --- a/posix/sched/src/lib.rs +++ b/posix/sched/src/lib.rs @@ -21,7 +21,7 @@ use ktask::{ KCpuMask, current, future::{block_on, interruptible, sleep}, }; -use kthread::{get_process_state, get_process_group}; +use kthread::{get_process_group, get_process_state}; use linux_raw_sys::general::{ __kernel_clockid_t, CLOCK_MONOTONIC, CLOCK_REALTIME, PRIO_PGRP, PRIO_PROCESS, PRIO_USER, SCHED_RR, TIMER_ABSTIME, timespec, diff --git a/posix/signal/src/lib.rs b/posix/signal/src/lib.rs index f2172c49..3ffae20a 100644 --- a/posix/signal/src/lib.rs +++ b/posix/signal/src/lib.rs @@ -153,7 +153,11 @@ pub fn sys_kill(pid: i32, signo: u32) -> KResult { match pid { 1.. => send_signal_to_process(pid as _, sig)?, 0 => { - let pgid = kthread::current_thread().process_state().proc.group().pgid(); + let pgid = kthread::current_thread() + .process_state() + .proc + .group() + .pgid(); send_signal_to_process_group(pgid, sig)?; } -1 => { @@ -205,9 +209,7 @@ pub fn make_queue_signal_info( // the value is fully initialized. let mut sig = unsafe { sig.read_uninit()?.assume_init() }; sig.set_signo(signo); - if kthread::current_thread().pid() != tgid - && (sig.code() >= 0 || sig.code() == SI_TKILL) - { + if kthread::current_thread().pid() != tgid && (sig.code() >= 0 || sig.code() == SI_TKILL) { return Err(KError::OperationNotPermitted); } Ok(Some(sig)) diff --git a/posix/sync/src/lib.rs b/posix/sync/src/lib.rs index a19387fc..e411f298 100644 --- a/posix/sync/src/lib.rs +++ b/posix/sync/src/lib.rs @@ -160,8 +160,7 @@ pub fn sys_set_robust_list(head: UserConstPtr, size: usize) -> if size != size_of::() { return Err(KError::InvalidInput); } - kthread::current_thread() - .set_robust_list_head(head.as_ptr() as usize); + kthread::current_thread().set_robust_list_head(head.as_ptr() as usize); Ok(0) } diff --git a/process/kresources/src/lib.rs b/process/kresources/src/lib.rs index e4f1056d..62b9d8a2 100644 --- a/process/kresources/src/lib.rs +++ b/process/kresources/src/lib.rs @@ -67,7 +67,10 @@ mod tests { fn test_process_resources_default_limits() { let resources = ProcessResources::new(0x80000); assert_eq!(resources.max_nofile(), 1024); - assert_eq!(resources.rlimits.read()[linux_raw_sys::general::RLIMIT_STACK].current, 0x80000); + assert_eq!( + resources.rlimits.read()[linux_raw_sys::general::RLIMIT_STACK].current, + 0x80000 + ); assert_eq!(resources.fd_table().read().count(), 0); } } diff --git a/process/kthread/src/lib.rs b/process/kthread/src/lib.rs index 4f34b845..c222d8c3 100644 --- a/process/kthread/src/lib.rs +++ b/process/kthread/src/lib.rs @@ -23,21 +23,21 @@ extern crate alloc; extern crate klogger; mod lifecycle_state; -mod process_state; mod posix_state; +mod process_state; mod registry; mod runtime_state; mod signal; mod stat; -mod timer; mod thread; +mod timer; pub use kfutex::{FutexEntry, FutexGuard, FutexKey, FutexTable, WaitQueue}; -pub use krlimit::{FILE_LIMIT, Rlimit, Rlimits}; pub use kresources::ProcessResources; +pub use krlimit::{FILE_LIMIT, Rlimit, Rlimits}; pub use lifecycle_state::ProcessLifecycleState; -pub use process_state::{ProcessState, ProcessStateConfig}; pub use posix_state::ProcessPosixState; +pub use process_state::{ProcessState, ProcessStateConfig}; pub use registry::{ add_task_to_table, cleanup_task_tables, get_process_group, get_process_state, get_session, get_task, processes, tasks, diff --git a/process/kthread/src/posix_state.rs b/process/kthread/src/posix_state.rs index cf3b9191..561fffde 100644 --- a/process/kthread/src/posix_state.rs +++ b/process/kthread/src/posix_state.rs @@ -4,11 +4,7 @@ //! POSIX-facing process-shared state. -use alloc::{ - string::String, - sync::Arc, - vec::Vec, -}; +use alloc::{string::String, sync::Arc, vec::Vec}; use core::sync::atomic::{AtomicU32, Ordering}; use ksignal::Signo; diff --git a/process/kthread/src/process_state.rs b/process/kthread/src/process_state.rs index d1115474..8b26cfb5 100644 --- a/process/kthread/src/process_state.rs +++ b/process/kthread/src/process_state.rs @@ -9,9 +9,9 @@ use alloc::{ }; use hashbrown::HashMap; +use kcred::Credentials; use kfd::{FdTable, register_current_fd_runtime}; use kfutex::{FutexKey, FutexTable}; -use kcred::Credentials; use kpoll::PollSet; use kprocess::Process; use kresources::ProcessResources; diff --git a/process/kthread/src/signal.rs b/process/kthread/src/signal.rs index 594e1f3d..c30b24b8 100644 --- a/process/kthread/src/signal.rs +++ b/process/kthread/src/signal.rs @@ -98,8 +98,8 @@ mod tests_signal { use super::{send_signal_to_process, send_signal_to_process_group, send_signal_to_thread}; use crate::{ - cleanup_task_tables, get_process_group, get_process_state, get_session, get_task, processes, - tasks, + cleanup_task_tables, get_process_group, get_process_state, get_session, get_task, + processes, tasks, }; #[def_test] diff --git a/process/kthread/src/thread/core.rs b/process/kthread/src/thread/core.rs index 1fa5d4c5..bcc32321 100644 --- a/process/kthread/src/thread/core.rs +++ b/process/kthread/src/thread/core.rs @@ -11,10 +11,10 @@ use core::{ use kprocess::Pid; use ksignal::api::ThreadSignalManager; -use ktask::KtaskRef; -use ktimer::TimeManager; #[cfg(feature = "tee")] use ksync::Mutex; +use ktask::KtaskRef; +use ktimer::TimeManager; #[cfg(feature = "tee")] use tee_task_iface::TeeSessionCtxTrait; diff --git a/process/kthread/src/thread/mod.rs b/process/kthread/src/thread/mod.rs index c4466b24..ea229487 100644 --- a/process/kthread/src/thread/mod.rs +++ b/process/kthread/src/thread/mod.rs @@ -7,5 +7,6 @@ mod current; mod task_ext; pub use core::{AssumeSync, CurrentThread, Thread}; + pub use current::{current_process_state, current_thread, with_current_thread}; pub use task_ext::AsThread; diff --git a/process/kthread/src/thread/task_ext.rs b/process/kthread/src/thread/task_ext.rs index ea735498..6882cb9e 100644 --- a/process/kthread/src/thread/task_ext.rs +++ b/process/kthread/src/thread/task_ext.rs @@ -50,12 +50,11 @@ unsafe impl TaskExt for Box { impl AsThread for TaskInner { fn try_as_thread(&self) -> Option<&Thread> { - self.task_ext() - .map(|ext| { - // SAFETY: The extension slot was populated with `Box` during - // thread creation (see `Thread::new`), so the concrete type is guaranteed - // to match the `downcast_ref` call. - unsafe { ext.downcast_ref::>() }.as_ref() - }) + self.task_ext().map(|ext| { + // SAFETY: The extension slot was populated with `Box` during + // thread creation (see `Thread::new`), so the concrete type is guaranteed + // to match the `downcast_ref` call. + unsafe { ext.downcast_ref::>() }.as_ref() + }) } } -- Gitee From 5955b5b801e4494818abea3c3ff3fcd745c116fe Mon Sep 17 00:00:00 2001 From: Weikang Guo Date: Thu, 7 May 2026 18:49:49 +0800 Subject: [PATCH 04/21] fix rusage bug Signed-off-by: Weikang Guo --- core/kcore/src/lib.rs | 1 - core/kcore/src/mm.rs | 2 +- core/kcore/src/task.rs | 7 ---- core/ksyscall/src/task/wait.rs | 17 +++++++++ posix/credentials/Cargo.toml | 2 +- posix/credentials/src/helpers.rs | 2 +- posix/process/src/lib.rs | 48 ++++++++++++++++++-------- process/kthread/src/lib.rs | 3 +- process/kthread/src/lifecycle_state.rs | 31 ++++++++++++++++- process/kthread/src/process_state.rs | 10 ++++++ 10 files changed, 94 insertions(+), 29 deletions(-) delete mode 100644 core/kcore/src/task.rs diff --git a/core/kcore/src/lib.rs b/core/kcore/src/lib.rs index b598475a..32cdb544 100644 --- a/core/kcore/src/lib.rs +++ b/core/kcore/src/lib.rs @@ -18,5 +18,4 @@ extern crate klogger; pub mod config; mod lrucache; pub mod mm; -pub mod task; pub mod vfs; diff --git a/core/kcore/src/mm.rs b/core/kcore/src/mm.rs index a7185137..90a80179 100644 --- a/core/kcore/src/mm.rs +++ b/core/kcore/src/mm.rs @@ -29,8 +29,8 @@ use ouroboros::self_referencing; use crate::{ config::{USER_SPACE_BASE, USER_SPACE_SIZE}, lrucache::LruCache, - task::AsThread, }; +use kthread::AsThread; /// Creates a new empty user address space. pub fn new_user_aspace_empty() -> KResult { diff --git a/core/kcore/src/task.rs b/core/kcore/src/task.rs deleted file mode 100644 index 704ca4af..00000000 --- a/core/kcore/src/task.rs +++ /dev/null @@ -1,7 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright 2025 KylinSoft Co., Ltd. -// See LICENSES for license details. - -//! User task management re-exported from [`kthread`]. - -pub use kthread::*; diff --git a/core/ksyscall/src/task/wait.rs b/core/ksyscall/src/task/wait.rs index b90d4c8f..c1d0c391 100644 --- a/core/ksyscall/src/task/wait.rs +++ b/core/ksyscall/src/task/wait.rs @@ -16,6 +16,7 @@ use bitflags::bitflags; use kerrno::{KError, KResult, LinuxError}; use kprocess::{Pid, Process}; use ktask::future::{block_on, interruptible}; +use kthread::{AsThread, get_process_state, get_task}; use linux_raw_sys::general::{ __WALL, __WCLONE, __WNOTHREAD, WCONTINUED, WEXITED, WNOHANG, WNOWAIT, WUNTRACED, }; @@ -98,6 +99,22 @@ pub fn sys_waitpid(pid: i32, exit_code: *mut i32, options: u32) -> KResult Rusage { }) } -fn children_rusage(proc: &Process) -> Rusage { - let current = current(); - proc.threads() +fn children_rusage(proc_state: &ProcessState) -> Rusage { + // Mirrors Linux kernel/sys.c:1872-1884: read accumulated reaped-children + // time (cutime/cstime equivalent) then add live children's current time. + let (reaped_utime_ns, reaped_stime_ns) = proc_state.child_time_ns(); + let reaped = Rusage { + utime: TimeValue::new( + reaped_utime_ns as u64 / 1_000_000_000, + (reaped_utime_ns as u64 % 1_000_000_000) as u32, + ), + stime: TimeValue::new( + reaped_stime_ns as u64 / 1_000_000_000, + (reaped_stime_ns as u64 % 1_000_000_000) as u32, + ), + }; + + let live = proc_state + .proc + .children() .into_iter() - .fold(Rusage::default(), |acc, child| { - if let Ok(task) = get_task(child) - && !current.ptr_eq(&task) - { - acc.collate(Rusage::from_thread(task.as_thread())) - } else { - acc - } - }) + .fold(Rusage::default(), |acc, child_proc| { + child_proc.threads().into_iter().fold(acc, |acc, tid| { + if let Ok(task) = get_task(tid) { + acc.collate(Rusage::from_thread(task.as_thread())) + } else { + acc + } + }) + }); + + reaped.collate(live) } /// Returns resource usage information for the current process, its children, or the current thread. @@ -164,10 +181,11 @@ pub fn sys_getrusage(who: i32, usage: *mut rusage) -> KResult { const RUSAGE_THREAD: i32 = linux_raw_sys::general::RUSAGE_THREAD as i32; let current_thread = kthread::current_thread(); - let proc = ¤t_thread.process_state().proc; + let proc_state = current_thread.process_state(); + let proc = &proc_state.proc; let result = match who { RUSAGE_SELF => self_rusage(proc), - RUSAGE_CHILDREN => children_rusage(proc), + RUSAGE_CHILDREN => children_rusage(proc_state), RUSAGE_THREAD => Rusage::from_thread(¤t_thread), _ => return Err(KError::InvalidInput), }; diff --git a/process/kthread/src/lib.rs b/process/kthread/src/lib.rs index c222d8c3..a02dc8a1 100644 --- a/process/kthread/src/lib.rs +++ b/process/kthread/src/lib.rs @@ -6,8 +6,7 @@ //! //! This crate owns the process-side thread/runtime surface: //! thread state, shared process state, task registries, and signal-delivery -//! glue. `kcore::task` now re-exports this surface instead of owning the -//! implementation directly. +//! glue. Consumers should depend on this crate directly. #![no_std] diff --git a/process/kthread/src/lifecycle_state.rs b/process/kthread/src/lifecycle_state.rs index c4b367ec..264289e8 100644 --- a/process/kthread/src/lifecycle_state.rs +++ b/process/kthread/src/lifecycle_state.rs @@ -5,14 +5,24 @@ //! Lifecycle state shared by all threads in a process. use alloc::sync::Arc; +use core::sync::atomic::{AtomicUsize, Ordering}; use kpoll::PollSet; /// Process lifecycle state shared by all threads in a process. -#[derive(Default)] pub struct ProcessLifecycleState { child_exit_event: Arc, exit_event: Arc, + /// Accumulated user-mode nanoseconds of reaped children. + child_utime_ns: AtomicUsize, + /// Accumulated kernel-mode nanoseconds of reaped children. + child_stime_ns: AtomicUsize, +} + +impl Default for ProcessLifecycleState { + fn default() -> Self { + Self::new() + } } impl ProcessLifecycleState { @@ -21,6 +31,8 @@ impl ProcessLifecycleState { Self { child_exit_event: Arc::default(), exit_event: Arc::default(), + child_utime_ns: AtomicUsize::new(0), + child_stime_ns: AtomicUsize::new(0), } } @@ -33,4 +45,21 @@ impl ProcessLifecycleState { pub fn exit_event(&self) -> &Arc { &self.exit_event } + + /// Adds reaped-child CPU time to the accumulated counters. + /// + /// Called when a child process is reaped via `wait`/`waitpid`. Mirrors + /// Linux `kernel/exit.c:1232-1233` where `psig->cutime += tgutime + sig->cutime`. + pub fn accumulate_child_time(&self, utime_ns: usize, stime_ns: usize) { + self.child_utime_ns.fetch_add(utime_ns, Ordering::Relaxed); + self.child_stime_ns.fetch_add(stime_ns, Ordering::Relaxed); + } + + /// Returns accumulated reaped-children user and kernel time in nanoseconds. + pub fn child_time_ns(&self) -> (usize, usize) { + ( + self.child_utime_ns.load(Ordering::Relaxed), + self.child_stime_ns.load(Ordering::Relaxed), + ) + } } diff --git a/process/kthread/src/process_state.rs b/process/kthread/src/process_state.rs index 8b26cfb5..c0cf7c9b 100644 --- a/process/kthread/src/process_state.rs +++ b/process/kthread/src/process_state.rs @@ -206,6 +206,16 @@ impl ProcessState { pub fn set_heap_top(&self, top: usize) { self.runtime.set_heap_top(top); } + + /// Adds reaped-child CPU time to the accumulated counters. + pub fn accumulate_child_time(&self, utime_ns: usize, stime_ns: usize) { + self.lifecycle.accumulate_child_time(utime_ns, stime_ns); + } + + /// Returns accumulated reaped-children user and kernel time in nanoseconds. + pub fn child_time_ns(&self) -> (usize, usize) { + self.lifecycle.child_time_ns() + } } pub(super) struct FutexTables { -- Gitee From 487f4c1d3d99ecf2d879f59c4e396e188fe5acad Mon Sep 17 00:00:00 2001 From: Weikang Guo Date: Thu, 7 May 2026 18:52:29 +0800 Subject: [PATCH 05/21] =?UTF-8?q?bugfix:=20sys=5Fprlimit64=20=E5=9C=A8=20h?= =?UTF-8?q?ard=20limit=20=E8=B6=85=E9=99=90=E6=97=B6=E9=9D=99=E9=BB=98?= =?UTF-8?q?=E8=BF=94=E5=9B=9E=20Ok(0)=EF=BC=8C=E5=85=81=E8=AE=B8=E4=BB=BB?= =?UTF-8?q?=E6=84=8F=E8=BF=9B=E7=A8=8B=E6=8F=90=E5=8D=87=20hard=20limit?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Weikang Guo --- core/ksyscall/src/resources.rs | 11 +++++------ core/ksyscall/src/task/wait.rs | 3 +-- posix/process/src/lib.rs | 3 +-- 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/core/ksyscall/src/resources.rs b/core/ksyscall/src/resources.rs index f5c0d47e..8690b019 100644 --- a/core/ksyscall/src/resources.rs +++ b/core/ksyscall/src/resources.rs @@ -39,14 +39,13 @@ pub fn sys_prlimit64( } let limit = &mut proc_state.resources.rlimits.write()[resource]; - if new_limit.rlim_max <= limit.max { - limit.max = new_limit.rlim_max; - } else { - // TODO: patch resources - // return Err(KError::OperationNotPermitted); - return Ok(0); + if new_limit.rlim_max > limit.max { + // Raising the hard limit requires CAP_SYS_RESOURCE. + // Return EPERM until proper credential checks are in place. + return Err(KError::OperationNotPermitted); } + limit.max = new_limit.rlim_max; limit.current = new_limit.rlim_cur; } diff --git a/core/ksyscall/src/task/wait.rs b/core/ksyscall/src/task/wait.rs index c1d0c391..70b98ee6 100644 --- a/core/ksyscall/src/task/wait.rs +++ b/core/ksyscall/src/task/wait.rs @@ -99,8 +99,7 @@ pub fn sys_waitpid(pid: i32, exit_code: *mut i32, options: u32) -> KResult Rusage { } fn children_rusage(proc_state: &ProcessState) -> Rusage { - // Mirrors Linux kernel/sys.c:1872-1884: read accumulated reaped-children - // time (cutime/cstime equivalent) then add live children's current time. + // Accumulated reaped-children time + live children's current time. let (reaped_utime_ns, reaped_stime_ns) = proc_state.child_time_ns(); let reaped = Rusage { utime: TimeValue::new( -- Gitee From 1e7d1fff255ee7e4bf266623100fda23af5c6b15 Mon Sep 17 00:00:00 2001 From: Weikang Guo Date: Thu, 7 May 2026 20:01:16 +0800 Subject: [PATCH 06/21] remove fdtable Once<> Signed-off-by: Weikang Guo --- core/kservices/src/file/epoll.rs | 16 +++-- core/kservices/src/file/fs.rs | 18 +++--- core/kservices/src/file/mod.rs | 2 +- core/kservices/src/file/net.rs | 6 +- core/kservices/src/task.rs | 2 +- core/kservices/src/vfs/dev/loop.rs | 4 +- core/ksyscall/Cargo.toml | 1 + core/ksyscall/src/fs/event.rs | 6 +- core/ksyscall/src/fs/pidfd.rs | 16 +++-- core/ksyscall/src/fs/signalfd.rs | 8 ++- core/ksyscall/src/fs/timerfd.rs | 12 +++- core/ksyscall/src/io_mpx/epoll.rs | 11 +++- core/ksyscall/src/io_mpx/poll.rs | 4 +- core/ksyscall/src/io_mpx/select.rs | 3 +- core/ksyscall/src/net/cmsg.rs | 9 ++- core/ksyscall/src/net/io.rs | 34 ++++++++--- core/ksyscall/src/net/name.rs | 6 +- core/ksyscall/src/net/opt.rs | 6 +- core/ksyscall/src/net/socket.rs | 35 ++++++++--- core/ksyscall/src/task/clone.rs | 5 +- posix/fs/src/ctl.rs | 9 ++- posix/fs/src/fd_ops.rs | 42 ++++++++----- posix/fs/src/io.rs | 91 +++++++++++++++++----------- posix/fs/src/open.rs | 6 +- posix/fs/src/path.rs | 10 ++- posix/fs/src/pipe.rs | 9 ++- posix/fs/src/stat.rs | 5 +- posix/mm/src/memfd.rs | 6 +- posix/mm/src/mmap.rs | 3 +- process/kfd/Cargo.toml | 1 - process/kfd/src/lib.rs | 61 +++++++------------ process/kthread/Cargo.toml | 1 - process/kthread/src/process_state.rs | 10 --- 33 files changed, 284 insertions(+), 174 deletions(-) diff --git a/core/kservices/src/file/epoll.rs b/core/kservices/src/file/epoll.rs index e469beaf..6b1dd277 100644 --- a/core/kservices/src/file/epoll.rs +++ b/core/kservices/src/file/epoll.rs @@ -18,8 +18,11 @@ use core::{ use bitflags::bitflags; use hashbrown::HashMap; use kerrno::{KError, KResult}; +use kfd::FdTable; use kpoll::{IoEvents, PollSet, Pollable}; use kspin::SpinNoPreempt; +use ksync::RwLock; +use kthread::current_process_state; use linux_raw_sys::general::{EPOLLET, EPOLLONESHOT, epoll_event}; use crate::file::{FileLike, get_file_like}; @@ -105,8 +108,8 @@ struct EntryKey { file: Weak, } impl EntryKey { - fn new(fd: i32) -> KResult { - let file = get_file_like(fd)?; + fn new(fd_table: &RwLock, fd: i32) -> KResult { + let file = get_file_like(fd_table, fd)?; Ok(Self { fd, file: Arc::downgrade(&file), @@ -317,7 +320,8 @@ impl Epoll { /// Adds a file descriptor interest to the epoll instance. pub fn add(&self, fd: i32, event: EpollEvent, flags: EpollFlags) -> KResult<()> { - let key = EntryKey::new(fd)?; + let fd_table = current_process_state().resources.fd_table(); + let key = EntryKey::new(&fd_table, fd)?; let interest = Arc::new(EpollInterest::new(key.clone(), event, flags)); let mut guard = self.inner.interests.lock(); if guard.contains_key(&key) { @@ -332,7 +336,8 @@ impl Epoll { /// Modifies an existing interest for the given file descriptor. pub fn modify(&self, fd: i32, event: EpollEvent, flags: EpollFlags) -> KResult<()> { - let key = EntryKey::new(fd)?; + let fd_table = current_process_state().resources.fd_table(); + let key = EntryKey::new(&fd_table, fd)?; let interest = Arc::new(EpollInterest::new(key.clone(), event, flags)); let mut guard = self.inner.interests.lock(); @@ -355,7 +360,8 @@ impl Epoll { /// Removes an existing interest for the given file descriptor. pub fn delete(&self, fd: i32) -> KResult<()> { - let key = EntryKey::new(fd)?; + let fd_table = current_process_state().resources.fd_table(); + let key = EntryKey::new(&fd_table, fd)?; self.inner .interests .lock() diff --git a/core/kservices/src/file/fs.rs b/core/kservices/src/file/fs.rs index d8aff3e3..60cae266 100644 --- a/core/kservices/src/file/fs.rs +++ b/core/kservices/src/file/fs.rs @@ -14,10 +14,12 @@ use core::{ use fs_ng_vfs::{Location, Metadata, NodeFlags}; use kerrno::{KError, KResult}; +use kfd::FdTable; use kfs::{FS_CONTEXT, FsContext}; use kpoll::{IoEvents, Pollable}; -use ksync::Mutex; +use ksync::{Mutex, RwLock}; use ktask::future::{block_on, poll_io}; +use kthread::current_process_state; use linux_raw_sys::general::{AT_EMPTY_PATH, AT_FDCWD, AT_SYMLINK_NOFOLLOW}; use super::{FileLike, Kstat, get_file_like}; @@ -32,7 +34,8 @@ pub fn with_fs(dirfd: c_int, f: impl FnOnce(&mut FsContext) -> KResult) -> if dirfd == AT_FDCWD { f(&mut fs) } else { - let dir = Directory::from_fd(dirfd)?.inner.clone(); + let fd_table = current_process_state().resources.fd_table(); + let dir = Directory::from_fd(&fd_table, dirfd)?.inner.clone(); f(&mut fs.with_current_dir(dir)?) } } @@ -75,7 +78,8 @@ pub fn resolve_at(dirfd: c_int, path: Option<&str>, flags: u32) -> KResult() { ResolveAtResult::File(file.inner().backend()?.location().clone()) @@ -212,11 +216,11 @@ impl FileLike for File { } /// Converts a file descriptor to a file reference. - fn from_fd(fd: c_int) -> KResult> + fn from_fd(fd_table: &RwLock, fd: c_int) -> KResult> where Self: Sized + 'static, { - get_file_like(fd)?.downcast_arc().map_err(|any| { + get_file_like(fd_table, fd)?.downcast_arc().map_err(|any| { if any.is::() { KError::IsADirectory } else { @@ -283,8 +287,8 @@ impl FileLike for Directory { } /// Converts a file descriptor to a directory reference. - fn from_fd(fd: c_int) -> KResult> { - get_file_like(fd)? + fn from_fd(fd_table: &RwLock, fd: c_int) -> KResult> { + get_file_like(fd_table, fd)? .downcast_arc() .map_err(|_| KError::NotADirectory) } diff --git a/core/kservices/src/file/mod.rs b/core/kservices/src/file/mod.rs index 72034577..fd067988 100644 --- a/core/kservices/src/file/mod.rs +++ b/core/kservices/src/file/mod.rs @@ -19,7 +19,7 @@ use kerrno::{KError, KResult}; use kfd::{FdTable, FileDescriptor}; pub use kfd::{ FileLike, IoDst, IoSrc, Kstat, ReadBuf, WriteBuf, add_file_like, close_all_fds, - close_file_like, current_fd_table, get_file_like, new_fd_table, + close_file_like, get_file_like, new_fd_table, }; use kfs::{FS_CONTEXT, OpenOptions}; use linux_raw_sys::general::{O_RDONLY, O_WRONLY}; diff --git a/core/kservices/src/file/net.rs b/core/kservices/src/file/net.rs index f026a069..6ed4a60f 100644 --- a/core/kservices/src/file/net.rs +++ b/core/kservices/src/file/net.rs @@ -8,11 +8,13 @@ use alloc::{borrow::Cow, format, sync::Arc}; use core::{ffi::c_int, ops::Deref, task::Context}; use kerrno::{KError, KResult}; +use kfd::FdTable; use knet::{ SocketOps, options::{Configurable, GetSocketOption, SetSocketOption}, }; use kpoll::{IoEvents, Pollable}; +use ksync::RwLock; use linux_raw_sys::general::{O_RDWR, S_IFSOCK}; use super::{FileLike, Kstat}; @@ -79,11 +81,11 @@ impl FileLike for Socket { } /// Converts a file descriptor to a socket reference. - fn from_fd(fd: c_int) -> KResult> + fn from_fd(fd_table: &RwLock, fd: c_int) -> KResult> where Self: Sized + 'static, { - get_file_like(fd)? + get_file_like(fd_table, fd)? .downcast_arc() .map_err(|_| KError::NotASocket) } diff --git a/core/kservices/src/task.rs b/core/kservices/src/task.rs index 0c56241b..eac7576b 100644 --- a/core/kservices/src/task.rs +++ b/core/kservices/src/task.rs @@ -204,7 +204,7 @@ pub fn do_exit(exit_code: i32, group_exit: bool) { // Close all file descriptors before marking the process as exited. // This ensures pipe write ends and other resources are properly released, // so parent processes blocking on pipe reads will receive EOF. - crate::file::close_all_fds(); + crate::file::close_all_fds(&thr.proc_state.resources.fd_table()); process.exit(); if let Some(parent) = process.parent() { diff --git a/core/kservices/src/vfs/dev/loop.rs b/core/kservices/src/vfs/dev/loop.rs index bde92d59..12a62ee9 100644 --- a/core/kservices/src/vfs/dev/loop.rs +++ b/core/kservices/src/vfs/dev/loop.rs @@ -12,6 +12,7 @@ use kcore::vfs::{DeviceMmap, DeviceOps}; use kerrno::{KError, KResult, LinuxError}; use kfs::FileBackend; use ksync::Mutex; +use kthread::current_process_state; use linux_raw_sys::{ ioctl::{BLKGETSIZE, BLKGETSIZE64, BLKRAGET, BLKRASET, BLKROGET, BLKROSET}, loop_device::{LOOP_CLR_FD, LOOP_GET_STATUS, LOOP_SET_FD, LOOP_SET_STATUS, loop_info}, @@ -91,7 +92,8 @@ impl DeviceOps for LoopDevice { if fd < 0 { return Err(KError::BadFileDescriptor); } - let f = get_file_like(fd)?; + let fd_table = current_process_state().resources.fd_table(); + let f = get_file_like(&fd_table, fd)?; let Some(file) = f.downcast_ref::() else { return Err(KError::InvalidInput); }; diff --git a/core/ksyscall/Cargo.toml b/core/ksyscall/Cargo.toml index 93ce61c9..6580b882 100644 --- a/core/ksyscall/Cargo.toml +++ b/core/ksyscall/Cargo.toml @@ -19,6 +19,7 @@ tee = [ [dependencies] kfd.workspace = true +ksync.workspace = true kservices = { workspace = true } kbuild_config = { workspace = true } kerrno.workspace = true diff --git a/core/ksyscall/src/fs/event.rs b/core/ksyscall/src/fs/event.rs index d00ba177..38454cd7 100644 --- a/core/ksyscall/src/fs/event.rs +++ b/core/ksyscall/src/fs/event.rs @@ -33,7 +33,11 @@ pub fn sys_eventfd2(initval: u32, flags: u32) -> KResult { let flags = EventFdFlags::from_bits(flags).ok_or(KError::InvalidInput)?; + let proc_state = kthread::current_process_state(); + let fd_table = proc_state.resources.fd_table(); + let max_nofile = proc_state.resources.max_nofile(); + let event_fd = EventFd::new(initval as _, flags.contains(EventFdFlags::SEMAPHORE)); event_fd.set_nonblocking(flags.contains(EventFdFlags::NONBLOCK))?; - add_file_like(event_fd as _, flags.contains(EventFdFlags::CLOEXEC)).map(|fd| fd as _) + add_file_like(&fd_table, max_nofile, event_fd as _, flags.contains(EventFdFlags::CLOEXEC)).map(|fd| fd as _) } diff --git a/core/ksyscall/src/fs/pidfd.rs b/core/ksyscall/src/fs/pidfd.rs index a3a74e30..81097491 100644 --- a/core/ksyscall/src/fs/pidfd.rs +++ b/core/ksyscall/src/fs/pidfd.rs @@ -35,7 +35,10 @@ pub fn sys_pidfd_open(pid: u32, flags: u32) -> KResult { let fd = PidFd::new(&task); // Add the pidfd to the current process's file descriptor table - fd.add_to_fd_table(true).map(|fd| fd as _) + let proc_state = kthread::current_process_state(); + let fd_table = proc_state.resources.fd_table(); + let max_nofile = proc_state.resources.max_nofile(); + fd.add_to_fd_table(&fd_table, max_nofile, true).map(|fd| fd as _) } /// Get a duplicate of a file descriptor from another process using its pidfd @@ -46,8 +49,9 @@ pub fn sys_pidfd_open(pid: u32, flags: u32) -> KResult { pub fn sys_pidfd_getfd(pidfd: i32, target_fd: i32, flags: u32) -> KResult { debug!("sys_pidfd_getfd <= pidfd: {pidfd}, target_fd: {target_fd}, flags: {flags}"); + let current_fd_table = kthread::current_process_state().resources.fd_table(); // Get the pidfd object and validate it - let pidfd = PidFd::from_fd(pidfd)?; + let pidfd = PidFd::from_fd(¤t_fd_table, pidfd)?; // Get the process state that this pidfd refers to let proc_state = pidfd.process_state()?; // Access the target process's file descriptor table within its scope @@ -60,7 +64,10 @@ pub fn sys_pidfd_getfd(pidfd: i32, target_fd: i32, flags: u32) -> KResult .ok_or(KError::BadFileDescriptor) // Duplicate the file and add it to current process's fd table .and_then(|fd| { - let fd = add_file_like(fd.inner.clone(), true)?; + let current_proc_state = kthread::current_process_state(); + let fd_table = current_proc_state.resources.fd_table(); + let max_nofile = current_proc_state.resources.max_nofile(); + let fd = add_file_like(&fd_table, max_nofile, fd.inner.clone(), true)?; Ok(fd as isize) }) } @@ -81,8 +88,9 @@ pub fn sys_pidfd_send_signal( return Err(KError::InvalidInput); } + let fd_table = kthread::current_process_state().resources.fd_table(); // Get the pidfd object and retrieve the process it refers to - let pidfd = PidFd::from_fd(pidfd)?; + let pidfd = PidFd::from_fd(&fd_table, pidfd)?; let pid = pidfd.process_state()?.proc.pid(); // Create signal info from user-provided data and send the signal diff --git a/core/ksyscall/src/fs/signalfd.rs b/core/ksyscall/src/fs/signalfd.rs index c384980c..66fbaedc 100644 --- a/core/ksyscall/src/fs/signalfd.rs +++ b/core/ksyscall/src/fs/signalfd.rs @@ -63,9 +63,12 @@ pub fn sys_signalfd4( // Read the signal mask from user space before handling the request mode. let mask = unsafe { mask.read_uninit()?.assume_init() }; + let proc_state = kthread::current_process_state(); + let fd_table = proc_state.resources.fd_table(); + // If fd is not -1, we should modify the existing signalfd if fd != -1 { - let signalfd = Signalfd::from_fd(fd)?; + let signalfd = Signalfd::from_fd(&fd_table, fd)?; signalfd.update_mask(mask); signalfd.set_nonblocking(flags.contains(SignalfdFlags::NONBLOCK))?; return Ok(fd as _); @@ -76,5 +79,6 @@ pub fn sys_signalfd4( signalfd.set_nonblocking(flags.contains(SignalfdFlags::NONBLOCK))?; // Add to file descriptor table - add_file_like(signalfd as _, flags.contains(SignalfdFlags::CLOEXEC)).map(|fd| fd as _) + let max_nofile = proc_state.resources.max_nofile(); + add_file_like(&fd_table, max_nofile, signalfd as _, flags.contains(SignalfdFlags::CLOEXEC)).map(|fd| fd as _) } diff --git a/core/ksyscall/src/fs/timerfd.rs b/core/ksyscall/src/fs/timerfd.rs index e2c84755..5cb45dc7 100644 --- a/core/ksyscall/src/fs/timerfd.rs +++ b/core/ksyscall/src/fs/timerfd.rs @@ -35,12 +35,16 @@ pub fn sys_timerfd_create(clock_id: i32, flags: u32) -> KResult { return Err(KError::InvalidInput); } + let proc_state = kthread::current_process_state(); + let fd_table = proc_state.resources.fd_table(); + let max_nofile = proc_state.resources.max_nofile(); + let tfd = TimerFd::new(clock_id as u32); if flags & TFD_NONBLOCK != 0 { tfd.set_nonblocking(true)?; } - add_file_like(tfd as _, flags & TFD_CLOEXEC != 0).map(|fd| fd as _) + add_file_like(&fd_table, max_nofile, tfd as _, flags & TFD_CLOEXEC != 0).map(|fd| fd as _) } /// Arms or disarms the timer referred to by `fd`. @@ -65,7 +69,8 @@ pub fn sys_timerfd_settime( let value = new.it_value.try_into_time_value()?; let interval = new.it_interval.try_into_time_value()?; - let tfd = TimerFd::from_fd(fd)?; + let fd_table = kthread::current_process_state().resources.fd_table(); + let tfd = TimerFd::from_fd(&fd_table, fd)?; let (old_interval, old_remaining) = tfd.settime(absolute, value, interval); if let Some(old_value) = old_value.check_non_null() { @@ -82,7 +87,8 @@ pub fn sys_timerfd_settime( pub fn sys_timerfd_gettime(fd: i32, curr_value: *mut itimerspec) -> KResult { debug!("sys_timerfd_gettime <= fd: {fd}"); - let tfd = TimerFd::from_fd(fd)?; + let fd_table = kthread::current_process_state().resources.fd_table(); + let tfd = TimerFd::from_fd(&fd_table, fd)?; let (interval, remaining) = tfd.gettime(); curr_value.write_vm(itimerspec { diff --git a/core/ksyscall/src/io_mpx/epoll.rs b/core/ksyscall/src/io_mpx/epoll.rs index f1ba3452..05d448a7 100644 --- a/core/ksyscall/src/io_mpx/epoll.rs +++ b/core/ksyscall/src/io_mpx/epoll.rs @@ -45,8 +45,11 @@ bitflags! { pub fn sys_epoll_create1(flags: u32) -> KResult { let flags = EpollCreateFlags::from_bits(flags).ok_or(KError::InvalidInput)?; debug!("sys_epoll_create1 <= flags: {flags:?}"); + let proc_state = kthread::current_process_state(); + let fd_table = proc_state.resources.fd_table(); + let max_nofile = proc_state.resources.max_nofile(); Epoll::new() - .add_to_fd_table(flags.contains(EpollCreateFlags::CLOEXEC)) + .add_to_fd_table(&fd_table, max_nofile, flags.contains(EpollCreateFlags::CLOEXEC)) .map(|fd| fd as isize) } @@ -57,7 +60,8 @@ pub fn sys_epoll_ctl( fd: i32, event: UserConstPtr, ) -> KResult { - let epoll = Epoll::from_fd(epfd)?; + let fd_table = kthread::current_process_state().resources.fd_table(); + let epoll = Epoll::from_fd(&fd_table, epfd)?; debug!("sys_epoll_ctl <= epfd: {epfd}, op: {op}, fd: {fd}"); let parse_event = || -> KResult<(EpollEvent, EpollFlags)> { @@ -102,7 +106,8 @@ fn do_epoll_wait( check_sigset_size(sigsetsize)?; debug!("sys_epoll_wait <= epfd: {epfd}, maxevents: {maxevents}, timeout: {timeout:?}"); - let epoll = Epoll::from_fd(epfd)?; + let fd_table = kthread::current_process_state().resources.fd_table(); + let epoll = Epoll::from_fd(&fd_table, epfd)?; if maxevents <= 0 { return Err(KError::InvalidInput); diff --git a/core/ksyscall/src/io_mpx/poll.rs b/core/ksyscall/src/io_mpx/poll.rs index 22b48bd4..0b63863c 100644 --- a/core/ksyscall/src/io_mpx/poll.rs +++ b/core/ksyscall/src/io_mpx/poll.rs @@ -12,7 +12,6 @@ use alloc::vec::Vec; use kerrno::{KError, KResult}; -use kfd::get_file_like; use khal::time::TimeValue; use kpoll::IoEvents; use kservices::{ @@ -36,6 +35,7 @@ fn do_poll( ) -> KResult { debug!("do_poll fds={poll_fds:?} timeout={timeout:?}"); + let fd_table = kthread::current_process_state().resources.fd_table(); let mut res = 0isize; let mut fds = Vec::with_capacity(poll_fds.len()); let mut revents = Vec::with_capacity(poll_fds.len()); @@ -44,7 +44,7 @@ fn do_poll( // Skip -1 continue; } - match get_file_like(fd.fd) { + match kfd::get_file_like(&fd_table, fd.fd) { Ok(f) => { fds.push(( f, diff --git a/core/ksyscall/src/io_mpx/select.rs b/core/ksyscall/src/io_mpx/select.rs index e30a6eec..7e30d926 100644 --- a/core/ksyscall/src/io_mpx/select.rs +++ b/core/ksyscall/src/io_mpx/select.rs @@ -14,7 +14,6 @@ use core::{fmt, time::Duration}; use bytemuck::AnyBitPattern; use kerrno::{KError, KResult}; -use kfd::current_fd_table; use kpoll::IoEvents; use kservices::{ mm::{UserConstPtr, UserPtr}, @@ -147,7 +146,7 @@ fn do_select( {except_fds:?}] timeout: {timeout:?}" ); - let fd_table = current_fd_table(); + let fd_table = kthread::current_process_state().resources.fd_table(); let fd_table = fd_table.read(); let mut fds = Vec::with_capacity(nfds); let mut fd_indices = Vec::with_capacity(nfds); diff --git a/core/ksyscall/src/net/cmsg.rs b/core/ksyscall/src/net/cmsg.rs index 96d52191..c7fa73c7 100644 --- a/core/ksyscall/src/net/cmsg.rs +++ b/core/ksyscall/src/net/cmsg.rs @@ -12,7 +12,7 @@ use core::{mem::size_of, net::SocketAddr, ptr}; use bytemuck::{NoUninit, bytes_of}; use kerrno::{KError, KResult, LinuxError}; -use kfd::{FileLike, get_file_like}; +use kfd::FileLike; use knet::UdpRecvError; use kservices::mm::{UserConstPtr, UserPtr}; use linux_raw_sys::net::{ @@ -120,7 +120,10 @@ pub enum CMsg { } impl CMsg { /// Parse a control message header and extract its data - pub fn parse(hdr: &cmsghdr) -> KResult { + pub fn parse( + fd_table: &ksync::RwLock, + hdr: &cmsghdr, + ) -> KResult { if hdr.cmsg_len < size_of::() { return Err(KError::InvalidInput); } @@ -139,7 +142,7 @@ impl CMsg { if fd < 0 { return Err(KError::BadFileDescriptor); } - let f = get_file_like(fd)?; + let f = kfd::get_file_like(fd_table, fd)?; fds.push(f); } Self::Rights { fds } diff --git a/core/ksyscall/src/net/io.rs b/core/ksyscall/src/net/io.rs index 4bd4a3c1..e10ffd20 100644 --- a/core/ksyscall/src/net/io.rs +++ b/core/ksyscall/src/net/io.rs @@ -14,6 +14,7 @@ use alloc::{boxed::Box, sync::Arc, vec::Vec}; use core::{any::TypeId, net::Ipv4Addr, time::Duration}; use kerrno::{KError, KResult, LinuxError}; +use kfd::FdTable; use khal::time::wall_time; use kio::prelude::*; use knet::{ @@ -42,7 +43,11 @@ fn parse_recvmmsg_timeout(timeout: UserConstPtr) -> KResult KResult> { +fn parse_send_cmsgs( + fd_table: &Arc>, + control_ptr: usize, + control_len: usize, +) -> KResult> { let mut cmsg = Vec::new(); if control_ptr == 0 || control_len == 0 { return Ok(cmsg); @@ -61,7 +66,7 @@ fn parse_send_cmsgs(control_ptr: usize, control_len: usize) -> KResult Option { None } -fn push_socket_cmsg(builder: &mut CMsgBuilder<'_>, cmsg: SocketCmsg) -> KResult { +fn push_socket_cmsg( + fd_table: &Arc>, + max_nofile: u64, + builder: &mut CMsgBuilder<'_>, + cmsg: SocketCmsg, +) -> KResult { match cmsg { SocketCmsg::Rights { fds } => builder.push(SOL_SOCKET, SCM_RIGHTS, |data| { let body_len = fds @@ -117,7 +127,7 @@ fn push_socket_cmsg(builder: &mut CMsgBuilder<'_>, cmsg: SocketCmsg) -> KResult< .into_iter() .zip(data[..body_len].chunks_exact_mut(size_of::())) { - let fd = add_file_like(f, false)?; + let fd = add_file_like(fd_table, max_nofile, f, false)?; chunk.copy_from_slice(&fd.to_ne_bytes()); written += size_of::(); } @@ -144,7 +154,8 @@ fn send_impl( debug!("sys_send <= fd: {fd}, flags: {flags}, addr: {addr:?}"); - let socket = Socket::from_fd(fd)?; + let fd_table = kthread::current_process_state().resources.fd_table(); + let socket = Socket::from_fd(&fd_table, fd)?; let sent = socket.send( &mut src, SendOptions { @@ -172,7 +183,8 @@ pub fn sys_sendto( /// Send data with vectored I/O and ancillary data (control messages) pub fn sys_sendmsg(fd: i32, msg: UserConstPtr, flags: u32) -> KResult { let msg = msg.get_as_ref()?; - let cmsg = parse_send_cmsgs(msg.msg_control as usize, msg.msg_controllen)?; + let fd_table = kthread::current_process_state().resources.fd_table(); + let cmsg = parse_send_cmsgs(&fd_table, msg.msg_control as usize, msg.msg_controllen)?; send_impl( fd, IoVectorBuf::new(msg.msg_iov as *const IoVec, msg.msg_iovlen)?.into_io(), @@ -195,7 +207,10 @@ fn recv_impl( ) -> KResult { debug!("sys_recv <= fd: {fd}, flags: {flags}"); - let socket = Socket::from_fd(fd)?; + let proc_state = kthread::current_process_state(); + let fd_table = proc_state.resources.fd_table(); + let max_nofile = proc_state.resources.max_nofile(); + let socket = Socket::from_fd(&fd_table, fd)?; let mut recv_flags = RecvFlags::empty(); if flags & MSG_PEEK != 0 { recv_flags |= RecvFlags::PEEK; @@ -231,7 +246,7 @@ fn recv_impl( warn!("received unexpected cmsg"); continue; }; - let push_result = push_socket_cmsg(&mut builder, cmsg); + let push_result = push_socket_cmsg(&fd_table, max_nofile, &mut builder, cmsg); match push_result { Ok(true) => {} @@ -311,9 +326,10 @@ pub fn sys_sendmmsg(fd: i32, msgvec: UserPtr, vlen: u32, flags: u32) -> } let msgvec = msgvec.get_as_mut_slice(vlen as usize)?; + let fd_table = kthread::current_process_state().resources.fd_table(); let mut sent = 0; for msg in msgvec.iter_mut() { - let cmsg = parse_send_cmsgs(msg.msg_hdr.msg_control as usize, msg.msg_hdr.msg_controllen)?; + let cmsg = parse_send_cmsgs(&fd_table, msg.msg_hdr.msg_control as usize, msg.msg_hdr.msg_controllen)?; match send_impl( fd, IoVectorBuf::new(msg.msg_hdr.msg_iov as *const IoVec, msg.msg_hdr.msg_iovlen)? diff --git a/core/ksyscall/src/net/name.rs b/core/ksyscall/src/net/name.rs index e30a1fc3..33b42a20 100644 --- a/core/ksyscall/src/net/name.rs +++ b/core/ksyscall/src/net/name.rs @@ -25,7 +25,8 @@ pub fn sys_getsockname( addr: UserPtr, addrlen: UserPtr, ) -> KResult { - let socket = Socket::from_fd(fd)?; + let fd_table = kthread::current_process_state().resources.fd_table(); + let socket = Socket::from_fd(&fd_table, fd)?; let local_addr = socket.local_addr()?; debug!("sys_getsockname <= fd: {fd}, addr: {local_addr:?}"); @@ -39,7 +40,8 @@ pub fn sys_getpeername( addr: UserPtr, addrlen: UserPtr, ) -> KResult { - let socket = Socket::from_fd(fd)?; + let fd_table = kthread::current_process_state().resources.fd_table(); + let socket = Socket::from_fd(&fd_table, fd)?; let peer_addr = socket.peer_addr()?; debug!("sys_getpeername <= fd: {fd}, addr: {peer_addr:?}"); diff --git a/core/ksyscall/src/net/opt.rs b/core/ksyscall/src/net/opt.rs index 71a307ec..d0bc19fc 100644 --- a/core/ksyscall/src/net/opt.rs +++ b/core/ksyscall/src/net/opt.rs @@ -149,7 +149,8 @@ pub fn sys_getsockopt( val.cast().get_as_mut() } - let socket = Socket::from_fd(fd)?; + let fd_table = kthread::current_process_state().resources.fd_table(); + let socket = Socket::from_fd(&fd_table, fd)?; macro_rules! dispatch { ($which:ident) => { socket.get_option(GetSocketOption::$which(get(optval, optlen)?))?; @@ -189,7 +190,8 @@ pub fn sys_setsockopt( val.cast().get_as_ref() } - let socket = Socket::from_fd(fd)?; + let fd_table = kthread::current_process_state().resources.fd_table(); + let socket = Socket::from_fd(&fd_table, fd)?; macro_rules! dispatch { ($which:ident) => { socket.set_option(SetSocketOption::$which(get(optval, optlen)?))?; diff --git a/core/ksyscall/src/net/socket.rs b/core/ksyscall/src/net/socket.rs index acc413e6..a02ee78b 100644 --- a/core/ksyscall/src/net/socket.rs +++ b/core/ksyscall/src/net/socket.rs @@ -105,14 +105,20 @@ pub fn sys_socket(domain: u32, raw_ty: u32, proto: u32) -> KResult { } let cloexec = raw_ty & O_CLOEXEC != 0; - socket.add_to_fd_table(cloexec).map(|fd| fd as isize) + let proc_state = kthread::current_process_state(); + let fd_table = proc_state.resources.fd_table(); + let max_nofile = proc_state.resources.max_nofile(); + socket + .add_to_fd_table(&fd_table, max_nofile, cloexec) + .map(|fd| fd as isize) } /// Bind a socket to a local address pub fn sys_bind(fd: i32, addr: UserConstPtr, addrlen: u32) -> KResult { let addr = SocketAddrEx::read_from_user(addr, addrlen)?; - Socket::from_fd(fd)?.bind(addr)?; + let fd_table = kthread::current_process_state().resources.fd_table(); + Socket::from_fd(&fd_table, fd)?.bind(addr)?; Ok(0) } @@ -121,7 +127,8 @@ pub fn sys_bind(fd: i32, addr: UserConstPtr, addrlen: u32) -> KResult< pub fn sys_connect(fd: i32, addr: UserConstPtr, addrlen: u32) -> KResult { let addr = SocketAddrEx::read_from_user(addr, addrlen)?; - Socket::from_fd(fd)?.connect(addr).map_err(|e| { + let fd_table = kthread::current_process_state().resources.fd_table(); + Socket::from_fd(&fd_table, fd)?.connect(addr).map_err(|e| { if e == KError::WouldBlock { KError::InProgress } else { @@ -138,7 +145,8 @@ pub fn sys_listen(fd: i32, backlog: i32) -> KResult { return Err(KError::InvalidInput); } - Socket::from_fd(fd)?.listen()?; + let fd_table = kthread::current_process_state().resources.fd_table(); + Socket::from_fd(&fd_table, fd)?.listen()?; Ok(0) } @@ -157,14 +165,19 @@ pub fn sys_accept4( ) -> KResult { let cloexec = flags & O_CLOEXEC != 0; - let socket = Socket::from_fd(fd)?; + let proc_state = kthread::current_process_state(); + let fd_table = proc_state.resources.fd_table(); + let max_nofile = proc_state.resources.max_nofile(); + let socket = Socket::from_fd(&fd_table, fd)?; let socket = Socket(socket.accept()?); if flags & O_NONBLOCK != 0 { socket.set_nonblocking(true)?; } let remote_addr = socket.peer_addr()?; - let fd = socket.add_to_fd_table(cloexec).map(|fd| fd as isize)?; + let fd = socket + .add_to_fd_table(&fd_table, max_nofile, cloexec) + .map(|fd| fd as isize)?; if !addr.is_null() { remote_addr.write_to_user(addr, addrlen.get_as_mut()?)?; @@ -175,7 +188,8 @@ pub fn sys_accept4( /// Shut down all or part of a full-duplex connection pub fn sys_shutdown(fd: i32, how: u32) -> KResult { - let socket = Socket::from_fd(fd)?; + let fd_table = kthread::current_process_state().resources.fd_table(); + let socket = Socket::from_fd(&fd_table, fd)?; let how = match how { SHUT_RD => Shutdown::Read, SHUT_WR => Shutdown::Write, @@ -222,9 +236,12 @@ pub fn sys_socketpair( } let cloexec = raw_ty & O_CLOEXEC != 0; + let proc_state = kthread::current_process_state(); + let fd_table = proc_state.resources.fd_table(); + let max_nofile = proc_state.resources.max_nofile(); *fds.get_as_mut()? = [ - sock1.add_to_fd_table(cloexec)?, - sock2.add_to_fd_table(cloexec)?, + sock1.add_to_fd_table(&fd_table, max_nofile, cloexec)?, + sock2.add_to_fd_table(&fd_table, max_nofile, cloexec)?, ]; Ok(0) } diff --git a/core/ksyscall/src/task/clone.rs b/core/ksyscall/src/task/clone.rs index c949df2a..f8adf139 100644 --- a/core/ksyscall/src/task/clone.rs +++ b/core/ksyscall/src/task/clone.rs @@ -313,7 +313,10 @@ impl CloneRequest { if self.flags.contains(CloneFlags::PIDFD) { let pidfd = PidFd::new(&new_proc_data); - (self.pidfd as *mut i32).write_vm(pidfd.add_to_fd_table(true)?)?; + let fd_table = old_proc_data.resources.fd_table(); + let max_nofile = old_proc_data.resources.max_nofile(); + (self.pidfd as *mut i32) + .write_vm(pidfd.add_to_fd_table(&fd_table, max_nofile, true)?)?; } let thr = Thread::new(tid, new_proc_data); diff --git a/posix/fs/src/ctl.rs b/posix/fs/src/ctl.rs index f06cebe4..c21376db 100644 --- a/posix/fs/src/ctl.rs +++ b/posix/fs/src/ctl.rs @@ -37,7 +37,8 @@ use crate::path::{resolve_at, with_fs}; /// of special files. pub fn sys_ioctl(fd: i32, cmd: u32, arg: usize) -> KResult { debug!("sys_ioctl <= fd: {fd}, cmd: {cmd}, arg: {arg}"); - let f = get_file_like(fd)?; + let fd_table = kthread::current_process_state().resources.fd_table(); + let f = get_file_like(&fd_table, fd)?; if cmd == FIONBIO { let val = (arg as *const u8).read_vm()?; if val != 0 && val != 1 { @@ -177,7 +178,8 @@ pub fn sys_getdents64(fd: i32, buf: UserPtr, len: usize) -> KResult { let mut buffer = DirBuffer::new(len); - let dir = Directory::from_fd(fd)?; + let fd_table = kthread::current_process_state().resources.fd_table(); + let dir = Directory::from_fd(&fd_table, fd)?; let mut dir_offset = dir.offset.lock(); let mut has_remaining = false; @@ -572,7 +574,8 @@ pub fn sys_sync() -> KResult { } pub fn sys_syncfs(fd: i32) -> KResult { - let file_like = get_file_like(fd)?; + let fd_table = kthread::current_process_state().resources.fd_table(); + let file_like = get_file_like(&fd_table, fd)?; if let Some(file) = file_like.downcast_ref::() { file.inner().location().filesystem().flush()?; diff --git a/posix/fs/src/fd_ops.rs b/posix/fs/src/fd_ops.rs index 436aec6f..0314cac1 100644 --- a/posix/fs/src/fd_ops.rs +++ b/posix/fs/src/fd_ops.rs @@ -14,7 +14,7 @@ use core::ffi::c_int; use bitflags::bitflags; use kerrno::{KError, KResult}; use kfd::{ - FileLike, add_file_like, close_file_like, current_fd_table, get_file_like, new_fd_table, + FileLike, add_file_like, close_file_like, get_file_like, new_fd_table, }; use kservices::file::Pipe; use linux_raw_sys::general::*; @@ -23,7 +23,9 @@ use posix_types::UserPtr; /// Closes the specified file descriptor. pub fn sys_close(fd: c_int) -> KResult { debug!("sys_close <= {fd}"); - close_file_like(fd)?; + let proc_state = kthread::current_process_state(); + let fd_table = proc_state.resources.fd_table(); + close_file_like(&fd_table, fd)?; Ok(0) } @@ -42,9 +44,9 @@ pub fn sys_close_range(first: i32, last: i32, flags: u32) -> KResult { } let flags = CloseRangeFlags::from_bits(flags).ok_or(KError::InvalidInput)?; debug!("sys_close_range <= fds: [{first}, {last}], flags: {flags:?}"); + + let proc_state = kthread::current_process_state(); if flags.contains(CloseRangeFlags::UNSHARE) { - let current_thread = kthread::current_thread(); - let proc_state = current_thread.process_state(); let old_files = proc_state.resources.fd_table(); let new_files = new_fd_table(); new_files.write().clone_from(&old_files.read()); @@ -52,7 +54,7 @@ pub fn sys_close_range(first: i32, last: i32, flags: u32) -> KResult { } let cloexec = flags.contains(CloseRangeFlags::CLOEXEC); - let fd_table = current_fd_table(); + let fd_table = proc_state.resources.fd_table(); let mut fd_table = fd_table.write(); if let Some(max_index) = fd_table.ids().next_back() { for fd in first..=last.min(max_index as i32) { @@ -71,8 +73,11 @@ pub fn sys_close_range(first: i32, last: i32, flags: u32) -> KResult { /// Duplicates a file descriptor and optionally sets `CLOEXEC`. fn dup_fd(old_fd: c_int, cloexec: bool) -> KResult { - let f = get_file_like(old_fd)?; - let new_fd = add_file_like(f, cloexec)?; + let proc_state = kthread::current_process_state(); + let fd_table = proc_state.resources.fd_table(); + let max_nofile = proc_state.resources.max_nofile(); + let f = get_file_like(&fd_table, old_fd)?; + let new_fd = add_file_like(&fd_table, max_nofile, f, cloexec)?; Ok(new_fd as _) } @@ -86,7 +91,8 @@ pub fn sys_dup(old_fd: c_int) -> KResult { /// Duplicates a file descriptor to a specific target fd. pub fn sys_dup2(old_fd: c_int, new_fd: c_int) -> KResult { if old_fd == new_fd { - get_file_like(new_fd)?; + let fd_table = kthread::current_process_state().resources.fd_table(); + get_file_like(&fd_table, new_fd)?; return Ok(new_fd as _); } sys_dup3(old_fd, new_fd, 0) @@ -108,7 +114,7 @@ pub fn sys_dup3(old_fd: c_int, new_fd: c_int, flags: c_int) -> KResult { return Err(KError::InvalidInput); } - let fd_table = current_fd_table(); + let fd_table = kthread::current_process_state().resources.fd_table(); let mut fd_table = fd_table.write(); let mut f = fd_table .get(old_fd as _) @@ -141,11 +147,13 @@ pub fn sys_fcntl(fd: c_int, cmd: c_int, arg: usize) -> KResult { Ok(0) } F_SETFL => { - get_file_like(fd)?.set_nonblocking(arg & (O_NONBLOCK as usize) > 0)?; + let fd_table = kthread::current_process_state().resources.fd_table(); + get_file_like(&fd_table, fd)?.set_nonblocking(arg & (O_NONBLOCK as usize) > 0)?; Ok(0) } F_GETFL => { - let f = get_file_like(fd)?; + let fd_table = kthread::current_process_state().resources.fd_table(); + let f = get_file_like(&fd_table, fd)?; let mut ret = f.open_flags(); if f.nonblocking() { @@ -155,7 +163,8 @@ pub fn sys_fcntl(fd: c_int, cmd: c_int, arg: usize) -> KResult { Ok(ret as _) } F_GETFD => { - let cloexec = current_fd_table() + let fd_table = kthread::current_process_state().resources.fd_table(); + let cloexec = fd_table .read() .get(fd as _) .ok_or(KError::BadFileDescriptor)? @@ -164,7 +173,8 @@ pub fn sys_fcntl(fd: c_int, cmd: c_int, arg: usize) -> KResult { } F_SETFD => { let cloexec = arg & FD_CLOEXEC as usize != 0; - current_fd_table() + let fd_table = kthread::current_process_state().resources.fd_table(); + fd_table .write() .get_mut(fd as _) .ok_or(KError::BadFileDescriptor)? @@ -172,11 +182,13 @@ pub fn sys_fcntl(fd: c_int, cmd: c_int, arg: usize) -> KResult { Ok(0) } F_GETPIPE_SZ => { - let pipe = Pipe::from_fd(fd)?; + let fd_table = kthread::current_process_state().resources.fd_table(); + let pipe = Pipe::from_fd(&fd_table, fd)?; Ok(pipe.capacity() as _) } F_SETPIPE_SZ => { - let pipe = Pipe::from_fd(fd)?; + let fd_table = kthread::current_process_state().resources.fd_table(); + let pipe = Pipe::from_fd(&fd_table, fd)?; pipe.resize(arg)?; Ok(0) } diff --git a/posix/fs/src/io.rs b/posix/fs/src/io.rs index 3da88689..2ecb6eb1 100644 --- a/posix/fs/src/io.rs +++ b/posix/fs/src/io.rs @@ -76,7 +76,10 @@ pub fn sys_dummy_fd(sysno: Sysno) -> KResult { return Err(KError::Unsupported); } warn!("Dummy fd created: {sysno}"); - DummyFd.add_to_fd_table(false).map(|fd| fd as isize) + let proc_state = kthread::current_process_state(); + let fd_table = proc_state.resources.fd_table(); + let max_nofile = proc_state.resources.max_nofile(); + DummyFd.add_to_fd_table(&fd_table, max_nofile, false).map(|fd| fd as isize) } /// Read data from the file indicated by `fd`. @@ -85,15 +88,17 @@ pub fn sys_dummy_fd(sysno: Sysno) -> KResult { pub fn sys_read(fd: i32, buf: UserPtr, len: usize) -> KResult { debug!("sys_read <= fd: {fd}, buf: {:p}, len: {len}", buf.as_ptr()); // Get the file object and perform the read operation into the user buffer - Ok(get_file_like(fd)?.read(&mut VmBytesMut::new(buf.as_ptr().cast_mut(), len))? as _) + let fd_table = kthread::current_process_state().resources.fd_table(); + Ok(get_file_like(&fd_table, fd)?.read(&mut VmBytesMut::new(buf, len))? as _) } /// Vectored read into multiple buffers. pub fn sys_readv(fd: i32, iov: UserConstPtr, iovcnt: usize) -> KResult { debug!("sys_readv <= fd: {fd}, iovcnt: {iovcnt}"); // Vectored read - read data into multiple buffers in a single operation - let f = get_file_like(fd)?; - f.read(&mut IoVectorBuf::new(iov.as_ptr(), iovcnt)?.into_io()) + let fd_table = kthread::current_process_state().resources.fd_table(); + let f = get_file_like(&fd_table, fd)?; + f.read(&mut IoVectorBuf::new(iov, iovcnt)?.into_io()) .map(|n| n as _) } @@ -102,15 +107,17 @@ pub fn sys_readv(fd: i32, iov: UserConstPtr, iovcnt: usize) -> KResult, len: usize) -> KResult { debug!("sys_write <= fd: {fd}, buf: {:p}, len: {len}", buf.as_ptr()); - Ok(get_file_like(fd)?.write(&mut VmBytes::new(buf.as_ptr(), len))? as _) + let fd_table = kthread::current_process_state().resources.fd_table(); + Ok(get_file_like(&fd_table, fd)?.write(&mut VmBytes::new(buf, len))? as _) } /// Vectored write from multiple buffers. pub fn sys_writev(fd: i32, iov: UserConstPtr, iovcnt: usize) -> KResult { debug!("sys_writev <= fd: {fd}, iovcnt: {iovcnt}"); // Vectored write - write data from multiple buffers in a single operation - let f = get_file_like(fd)?; - f.write(&mut IoVectorBuf::new(iov.as_ptr(), iovcnt)?.into_io()) + let fd_table = kthread::current_process_state().resources.fd_table(); + let f = get_file_like(&fd_table, fd)?; + f.write(&mut IoVectorBuf::new(iov, iovcnt)?.into_io()) .map(|n| n as _) } @@ -124,7 +131,8 @@ pub fn sys_lseek(fd: c_int, offset: __kernel_off_t, whence: c_int) -> KResult SeekFrom::End(offset as _), _ => return Err(KError::InvalidInput), }; - let any_file = get_file_like(fd)?; + let fd_table = kthread::current_process_state().resources.fd_table(); + let any_file = get_file_like(&fd_table, fd)?; if let Ok(f) = any_file.clone().downcast_arc::() { let off = f.inner().seek(pos)?; @@ -171,7 +179,8 @@ pub fn sys_truncate(path: UserConstPtr, length: __kernel_off_t) -> KResu pub fn sys_ftruncate(fd: c_int, length: __kernel_off_t) -> KResult { debug!("sys_ftruncate <= {fd} {length}"); // Truncate file descriptor to specified length - let f = File::from_fd(fd)?; + let fd_table = kthread::current_process_state().resources.fd_table(); + let f = File::from_fd(&fd_table, fd)?; f.inner().access(FileFlags::WRITE)?.set_len(length as _)?; Ok(0) } @@ -209,7 +218,8 @@ pub fn sys_fallocate( let keep_size = (mode & FALLOC_FL_KEEP_SIZE) != 0; let base_mode = mode & !FALLOC_FL_KEEP_SIZE; - let f = File::from_fd(fd)?; + let fd_table = kthread::current_process_state().resources.fd_table(); + let f = File::from_fd(&fd_table, fd)?; let inner = f.inner(); let file = inner.access(FileFlags::WRITE)?; @@ -322,7 +332,8 @@ pub fn sys_fallocate( pub fn sys_fsync(fd: c_int) -> KResult { debug!("sys_fsync <= {fd}"); // Synchronize file to disk - syncs both data and metadata - let any_file = get_file_like(fd)?; + let fd_table = kthread::current_process_state().resources.fd_table(); + let any_file = get_file_like(&fd_table, fd)?; if let Ok(f) = any_file.clone().downcast_arc::() { f.inner().sync(false)?; return Ok(0); @@ -337,7 +348,8 @@ pub fn sys_fsync(fd: c_int) -> KResult { pub fn sys_fdatasync(fd: c_int) -> KResult { debug!("sys_fdatasync <= {fd}"); // Synchronize file data to disk - only syncs data, not metadata - let any_file = get_file_like(fd)?; + let fd_table = kthread::current_process_state().resources.fd_table(); + let any_file = get_file_like(&fd_table, fd)?; if let Ok(f) = any_file.clone().downcast_arc::() { f.inner().sync(true)?; return Ok(0); @@ -358,7 +370,8 @@ pub fn sys_fadvise64( debug!("sys_fadvise64 <= fd: {fd}, offset: {offset}, len: {len}, advice: {advice}"); // Provide hints to kernel about how file will be accessed // Currently not fully implemented - pipes are not supported - if Pipe::from_fd(fd).is_ok() { + let fd_table = kthread::current_process_state().resources.fd_table(); + if Pipe::from_fd(&fd_table, fd).is_ok() { return Err(KError::BrokenPipe); } if advice > 5 { @@ -375,7 +388,8 @@ pub fn sys_pread64( offset: __kernel_off_t, ) -> KResult { // Read from file at specific offset without changing file position - let f = File::from_fd(fd)?; + let fd_table = kthread::current_process_state().resources.fd_table(); + let f = File::from_fd(&fd_table, fd)?; if offset < 0 { return Err(KError::InvalidInput); } @@ -396,10 +410,9 @@ pub fn sys_pwrite64( if len == 0 { return Ok(0); } - let f = File::from_fd(fd)?; - let write = f - .inner() - .write_at(VmBytes::new(buf.as_ptr(), len), offset as _)?; + let fd_table = kthread::current_process_state().resources.fd_table(); + let f = File::from_fd(&fd_table, fd)?; + let write = f.inner().write_at(VmBytes::new(buf, len), offset as _)?; Ok(write as _) } @@ -435,7 +448,8 @@ pub fn sys_preadv2( ) -> KResult { debug!("sys_preadv2 <= fd: {fd}, iovcnt: {iovcnt}, offset: {offset}, flags: {_flags}"); // Vectored read at specific offset with optional flags - let f = File::from_fd(fd)?; + let fd_table = kthread::current_process_state().resources.fd_table(); + let f = File::from_fd(&fd_table, fd)?; f.inner() .read_at( IoVectorBuf::new(iov.as_ptr(), iovcnt)?.into_io(), @@ -454,7 +468,8 @@ pub fn sys_pwritev2( ) -> KResult { debug!("sys_pwritev2 <= fd: {fd}, iovcnt: {iovcnt}, offset: {offset}, flags: {_flags}"); // Vectored write at specific offset with optional flags. - let f = File::from_fd(fd)?; + let fd_table = kthread::current_process_state().resources.fd_table(); + let f = File::from_fd(&fd_table, fd)?; f.inner() .write_at( IoVectorBuf::new(iov.as_ptr(), iovcnt)?.into_io(), @@ -562,18 +577,19 @@ pub fn sys_sendfile( ); // Source can use fixed offset or current file position + let fd_table = kthread::current_process_state().resources.fd_table(); let src = if !offset.is_null() { // Check offset fits in 32-bit range (legacy syscall limitation) if offset.read_vm()? > u32::MAX as u64 { return Err(KError::InvalidInput); } - SendFile::Offset(File::from_fd(in_fd)?, offset) + SendFile::Offset(File::from_fd(&fd_table, in_fd)?, offset) } else { - SendFile::Direct(get_file_like(in_fd)?) + SendFile::Direct(get_file_like(&fd_table, in_fd)?) }; // Destination always uses current file position - let dst = SendFile::Direct(get_file_like(out_fd)?); + let dst = SendFile::Direct(get_file_like(&fd_table, out_fd)?); do_send(src, dst, len).map(|n| n as _) } @@ -603,17 +619,18 @@ pub fn sys_copy_file_range( // TODO: check same file and overlap // Source can use fixed offset or current file position + let fd_table = kthread::current_process_state().resources.fd_table(); let src = if !off_in.is_null() { - SendFile::Offset(File::from_fd(fd_in)?, off_in) + SendFile::Offset(File::from_fd(&fd_table, fd_in)?, off_in) } else { - SendFile::Direct(get_file_like(fd_in)?) + SendFile::Direct(get_file_like(&fd_table, fd_in)?) }; // Destination can also use fixed offset or current file position let dst = if !off_out.is_null() { - SendFile::Offset(File::from_fd(fd_out)?, off_out) + SendFile::Offset(File::from_fd(&fd_table, fd_out)?, off_out) } else { - SendFile::Direct(get_file_like(fd_out)?) + SendFile::Direct(get_file_like(&fd_table, fd_out)?) }; do_send(src, dst, len).map(|n| n as _) @@ -642,8 +659,10 @@ pub fn sys_splice( // Track if we have a pipe - at least one must be present for splice let mut has_pipe = false; + let fd_table = kthread::current_process_state().resources.fd_table(); + // Dummy file descriptors cannot be spliced - if DummyFd::from_fd(fd_in).is_ok() || DummyFd::from_fd(fd_out).is_ok() { + if DummyFd::from_fd(&fd_table, fd_in).is_ok() || DummyFd::from_fd(&fd_table, fd_out).is_ok() { return Err(KError::BadFileDescriptor); } @@ -653,10 +672,10 @@ pub fn sys_splice( if off_in.read_vm()? < 0 { return Err(KError::InvalidInput); } - SendFile::Offset(File::from_fd(fd_in)?, off_in.cast()) + SendFile::Offset(File::from_fd(&fd_table, fd_in)?, off_in.cast()) } else { // Try to use as pipe first - if let Ok(src) = Pipe::from_fd(fd_in) { + if let Ok(src) = Pipe::from_fd(&fd_table, fd_in) { // Pipe must be readable if !src.is_read() { return Err(KError::BadFileDescriptor); @@ -664,12 +683,12 @@ pub fn sys_splice( has_pipe = true; } // Path-only files (opened without O_RDWR/O_WRONLY) cannot be spliced - if let Ok(file) = File::from_fd(fd_in) + if let Ok(file) = File::from_fd(&fd_table, fd_in) && file.inner().is_path() { return Err(KError::InvalidInput); } - SendFile::Direct(get_file_like(fd_in)?) + SendFile::Direct(get_file_like(&fd_table, fd_in)?) }; // Setup destination: either with fixed offset or using current position @@ -678,10 +697,10 @@ pub fn sys_splice( if off_out.read_vm()? < 0 { return Err(KError::InvalidInput); } - SendFile::Offset(File::from_fd(fd_out)?, off_out.cast()) + SendFile::Offset(File::from_fd(&fd_table, fd_out)?, off_out.cast()) } else { // Try to use as pipe first - if let Ok(dst) = Pipe::from_fd(fd_out) { + if let Ok(dst) = Pipe::from_fd(&fd_table, fd_out) { // Pipe must be writable if !dst.is_write() { return Err(KError::BadFileDescriptor); @@ -689,13 +708,13 @@ pub fn sys_splice( has_pipe = true; } // APPEND mode files cannot be spliced (offset cannot be changed) - if let Ok(file) = File::from_fd(fd_out) + if let Ok(file) = File::from_fd(&fd_table, fd_out) && file.inner().access(FileFlags::APPEND).is_ok() { return Err(KError::InvalidInput); } // Verify destination is writable with a write probe - let f = get_file_like(fd_out)?; + let f = get_file_like(&fd_table, fd_out)?; f.write(&mut b"".as_slice())?; SendFile::Direct(f) }; diff --git a/posix/fs/src/open.rs b/posix/fs/src/open.rs index a8a3738e..2cfd5cbd 100644 --- a/posix/fs/src/open.rs +++ b/posix/fs/src/open.rs @@ -15,6 +15,7 @@ use kservices::{ file::{Directory, File, FileLike, add_file_like}, vfs::dev::tty, }; +use kthread::current_process_state; use linux_raw_sys::general::*; use posix_types::UserConstPtr; @@ -104,7 +105,10 @@ fn add_to_fd(result: OpenResult, flags: u32) -> KResult { if flags & O_NONBLOCK != 0 { f.set_nonblocking(true)?; } - add_file_like(f, flags & O_CLOEXEC != 0) + let proc_state = current_process_state(); + let fd_table = proc_state.resources.fd_table(); + let max_nofile = proc_state.resources.max_nofile(); + add_file_like(&fd_table, max_nofile, f, flags & O_CLOEXEC != 0) } /// Opens a file relative to a directory file descriptor. diff --git a/posix/fs/src/path.rs b/posix/fs/src/path.rs index 9c781a4b..7b41be7a 100644 --- a/posix/fs/src/path.rs +++ b/posix/fs/src/path.rs @@ -12,7 +12,7 @@ use kerrno::{KError, KResult}; use kfd::{FileLike, Kstat, get_file_like}; use kfs::{FS_CONTEXT, FsContext}; use kservices::file::{Directory, File}; -use kthread::{current_thread, get_process_state}; +use kthread::{current_process_state, current_thread, get_process_state}; use linux_raw_sys::general::{AT_EMPTY_PATH, AT_FDCWD, AT_SYMLINK_NOFOLLOW, O_NOFOLLOW, O_PATH}; /// The coarse shape of a path string before any runtime resolution. @@ -117,7 +117,9 @@ pub fn with_fs(dirfd: c_int, f: impl FnOnce(&mut FsContext) -> KResult) -> if dirfd == AT_FDCWD { f(&mut fs) } else { - let dir = Directory::from_fd(dirfd)?.inner().clone(); + let proc_state = current_process_state(); + let fd_table = proc_state.resources.fd_table(); + let dir = Directory::from_fd(&fd_table, dirfd)?.inner().clone(); f(&mut fs.with_current_dir(dir)?) } } @@ -228,7 +230,9 @@ fn resolve_empty_path(dirfd: c_int, flags: u32) -> KResult { if flags & AT_EMPTY_PATH == 0 { return Err(KError::NotFound); } - let file_like = get_file_like(dirfd)?; + let proc_state = current_process_state(); + let fd_table = proc_state.resources.fd_table(); + let file_like = get_file_like(&fd_table, dirfd)?; ResolvedPath::from_file_like(file_like.path().into_owned(), file_like).map(PathSource::Resolved) } diff --git a/posix/fs/src/pipe.rs b/posix/fs/src/pipe.rs index 6074cc2b..a8b78f7d 100644 --- a/posix/fs/src/pipe.rs +++ b/posix/fs/src/pipe.rs @@ -39,16 +39,19 @@ pub fn sys_pipe2(fds: UserPtr<[c_int; 2]>, flags: u32) -> KResult { new_flags }; + let proc_state = kthread::current_process_state(); + let fd_table = proc_state.resources.fd_table(); + let max_nofile = proc_state.resources.max_nofile(); let cloexec = flags.contains(PipeFlags::CLOEXEC); let (read_end, write_end) = Pipe::new(); if flags.contains(PipeFlags::NONBLOCK) { read_end.set_nonblocking(true)?; write_end.set_nonblocking(true)?; } - let read_fd = read_end.add_to_fd_table(cloexec)?; + let read_fd = read_end.add_to_fd_table(&fd_table, max_nofile, cloexec)?; let write_fd = write_end - .add_to_fd_table(cloexec) - .inspect_err(|_| close_file_like(read_fd).unwrap())?; + .add_to_fd_table(&fd_table, max_nofile, cloexec) + .inspect_err(|_| close_file_like(&fd_table, read_fd).unwrap())?; fds.write_vm([read_fd, write_fd])?; diff --git a/posix/fs/src/stat.rs b/posix/fs/src/stat.rs index f5d07907..aadfee4c 100644 --- a/posix/fs/src/stat.rs +++ b/posix/fs/src/stat.rs @@ -11,6 +11,7 @@ use kerrno::{KError, KResult}; use kfd::FileLike; use kfs::FS_CONTEXT; use kservices::file::File; +use kthread::current_process_state; use linux_raw_sys::general::{ __kernel_fsid_t, AT_EMPTY_PATH, R_OK, W_OK, X_OK, stat, statfs, statx, }; @@ -160,6 +161,8 @@ pub fn sys_statfs(path: UserConstPtr, buf: UserPtr) -> KResult) -> KResult { debug!("sys_fstatfs <= fd: {fd}"); - buf.write_vm(statfs(File::from_fd(fd)?.inner().location())?)?; + let proc_state = current_process_state(); + let fd_table = proc_state.resources.fd_table(); + buf.write_vm(statfs(File::from_fd(&fd_table, fd)?.inner().location())?)?; Ok(0) } diff --git a/posix/mm/src/memfd.rs b/posix/mm/src/memfd.rs index ae26d678..9f21877b 100644 --- a/posix/mm/src/memfd.rs +++ b/posix/mm/src/memfd.rs @@ -10,6 +10,7 @@ use core::ffi::c_char; use kerrno::{KError, KResult}; use kfs::{FS_CONTEXT, OpenOptions}; use kservices::file::{File, FileLike}; +use kthread::current_process_state; use linux_raw_sys::general::{MFD_CLOEXEC, O_RDWR}; use posix_types::UserConstPtr; @@ -29,8 +30,11 @@ pub fn sys_memfd_create(_name: UserConstPtr, flags: u32) -> KResult 0 { - Some(File::from_fd(fd)?) + let fd_table = proc_state.resources.fd_table(); + Some(File::from_fd(&fd_table, fd)?) } else { None }; diff --git a/process/kfd/Cargo.toml b/process/kfd/Cargo.toml index 660a7960..6577624e 100644 --- a/process/kfd/Cargo.toml +++ b/process/kfd/Cargo.toml @@ -18,6 +18,5 @@ kio.workspace = true kpoll.workspace = true krlimit.workspace = true ksync.workspace = true -ktypes.workspace = true linux-raw-sys = { workspace = true, features = ["general"] } unittest.workspace = true diff --git a/process/kfd/src/lib.rs b/process/kfd/src/lib.rs index 29e5e7a9..939bd97f 100644 --- a/process/kfd/src/lib.rs +++ b/process/kfd/src/lib.rs @@ -24,7 +24,6 @@ use kerrno::{KError, KResult}; use kio::prelude::*; use kpoll::Pollable; use ksync::RwLock; -use ktypes::Once; use linux_raw_sys::general::{ S_IFMT, S_IFREG, STATX_ATTR_WRITE_ATOMIC, STATX_WRITE_ATOMIC, stat, statx, statx_timestamp, }; @@ -185,20 +184,25 @@ pub trait FileLike: Pollable + DowncastSync { Ok(()) } - fn from_fd(fd: c_int) -> KResult> + fn from_fd(fd_table: &RwLock, fd: c_int) -> KResult> where Self: Sized + 'static, { - get_file_like(fd)? + get_file_like(fd_table, fd)? .downcast_arc() .map_err(|_| KError::InvalidInput) } - fn add_to_fd_table(self, cloexec: bool) -> KResult + fn add_to_fd_table( + self, + fd_table: &RwLock, + max_nofile: u64, + cloexec: bool, + ) -> KResult where Self: Sized + 'static, { - add_file_like(Arc::new(self), cloexec) + add_file_like(fd_table, max_nofile, Arc::new(self), cloexec) } } impl_downcast!(sync FileLike); @@ -213,18 +217,6 @@ pub struct FileDescriptor { /// The current process-local file descriptor table type. pub type FdTable = FlattenObjects; -static CURRENT_FD_TABLE: Once Arc>> = Once::new(); -static CURRENT_NOFILE_LIMIT: Once u64> = Once::new(); - -/// Registers current-process fd runtime accessors. -pub fn register_current_fd_runtime( - current_fd_table: fn() -> Arc>, - current_nofile_limit: fn() -> u64, -) { - CURRENT_FD_TABLE.call_once(|| current_fd_table); - CURRENT_NOFILE_LIMIT.call_once(|| current_nofile_limit); -} - /// Creates a fresh file descriptor table. pub fn new_fd_table() -> Arc> { let mut table = Arc::>::new_uninit(); @@ -237,28 +229,22 @@ pub fn new_fd_table() -> Arc> { unsafe { table.assume_init() } } -/// Returns the current process's file descriptor table. -pub fn current_fd_table() -> Arc> { - CURRENT_FD_TABLE - .get() - .expect("fd runtime must be registered before use")() -} - /// Retrieves a file-like object from the file descriptor table. -pub fn get_file_like(fd: c_int) -> KResult> { - current_fd_table() +pub fn get_file_like(fd_table: &RwLock, fd: c_int) -> KResult> { + fd_table .read() .get(fd as usize) .map(|fd| fd.inner.clone()) .ok_or(KError::BadFileDescriptor) } -/// Adds a file-like object to the current process's file descriptor table. -pub fn add_file_like(f: Arc, cloexec: bool) -> KResult { - let max_nofile = CURRENT_NOFILE_LIMIT - .get() - .expect("fd runtime must be registered before use")(); - let fd_table = current_fd_table(); +/// Adds a file-like object to the file descriptor table. +pub fn add_file_like( + fd_table: &RwLock, + max_nofile: u64, + f: Arc, + cloexec: bool, +) -> KResult { let mut table = fd_table.write(); if table.count() as u64 >= max_nofile { return Err(KError::TooManyOpenFiles); @@ -268,18 +254,17 @@ pub fn add_file_like(f: Arc, cloexec: bool) -> KResult { } /// Closes a file descriptor and removes it from the file descriptor table. -pub fn close_file_like(fd: c_int) -> KResult { - current_fd_table() +pub fn close_file_like(fd_table: &RwLock, fd: c_int) -> KResult { + fd_table .write() .remove(fd as usize) .ok_or(KError::BadFileDescriptor)?; Ok(()) } -/// Close all open file descriptors for the current process. -pub fn close_all_fds() { - let fd_table = current_fd_table(); - if Arc::strong_count(&fd_table) > 1 { +/// Closes all file descriptors in the table if it is not shared. +pub fn close_all_fds(fd_table: &Arc>) { + if Arc::strong_count(fd_table) > 1 { return; } diff --git a/process/kthread/Cargo.toml b/process/kthread/Cargo.toml index 7b7eb765..95fc01cf 100644 --- a/process/kthread/Cargo.toml +++ b/process/kthread/Cargo.toml @@ -17,7 +17,6 @@ extern-trait.workspace = true hashbrown.workspace = true kcred.workspace = true kerrno.workspace = true -kfd.workspace = true klogger.workspace = true kpoll.workspace = true kfutex.workspace = true diff --git a/process/kthread/src/process_state.rs b/process/kthread/src/process_state.rs index c0cf7c9b..71646bf2 100644 --- a/process/kthread/src/process_state.rs +++ b/process/kthread/src/process_state.rs @@ -10,7 +10,6 @@ use alloc::{ use hashbrown::HashMap; use kcred::Credentials; -use kfd::{FdTable, register_current_fd_runtime}; use kfutex::{FutexKey, FutexTable}; use kpoll::PollSet; use kprocess::Process; @@ -68,14 +67,6 @@ pub struct ProcessState { pub tee_ta_ctx: RwLock, } -fn current_fd_table() -> Arc> { - crate::current_process_state().resources.fd_table() -} - -fn current_nofile_limit() -> u64 { - crate::current_process_state().resources.max_nofile() -} - impl ProcessState { /// Creates a new [`ProcessState`]. #[allow(clippy::too_many_arguments)] @@ -89,7 +80,6 @@ impl ProcessState { credentials: Credentials, config: ProcessStateConfig, ) -> Arc { - register_current_fd_runtime(current_fd_table, current_nofile_limit); #[cfg(feature = "tee")] let tee_ta_ctx = RwLock::new(TeeTaCtx::new(&exe_path)); let posix = ProcessPosixState::new(exe_path, cmdline, exit_signal); -- Gitee From 1a6200f2336b26544a6c4cbc3d159563471297f7 Mon Sep 17 00:00:00 2001 From: Weikang Guo Date: Thu, 7 May 2026 20:23:12 +0800 Subject: [PATCH 07/21] Move user-space VA layout constants to kaddr_layout Add UserLayoutConsts alongside kernel LayoutConsts in kaddr_layout, consolidating user-space address layout definitions (USER_HEAP_BASE, USER_STACK_SIZE, SIGNAL_TRAMPOLINE, etc.) into the crate that owns virtual memory layout. kcore::config re-exports them for backward compatibility. ProcessStateConfig now implements Default using kaddr_layout constants, eliminating duplicate config construction. --- core/kcore/Cargo.toml | 1 + core/kcore/src/config/aarch64.rs | 31 +------------------- core/kcore/src/config/loongarch64.rs | 31 +------------------- core/kcore/src/config/mod.rs | 10 +++++++ core/kcore/src/config/riscv64.rs | 35 ++++------------------ core/kcore/src/config/x86_64.rs | 31 +------------------- core/kservices/src/unittest_task.rs | 6 +--- core/ksyscall/src/dispatch.rs | 6 ++-- core/ksyscall/src/task/clone.rs | 6 +--- entry/src/entry.rs | 6 +--- mm/kaddr_layout/src/aarch64.rs | 26 +++++++++++++++- mm/kaddr_layout/src/fallback.rs | 14 ++++++++- mm/kaddr_layout/src/lib.rs | 44 ++++++++++++++++++++++++++++ mm/kaddr_layout/src/loongarch64.rs | 26 +++++++++++++++- mm/kaddr_layout/src/riscv64.rs | 26 +++++++++++++++- mm/kaddr_layout/src/x86_64.rs | 28 +++++++++++++++++- posix/mm/Cargo.toml | 1 + posix/mm/src/brk.rs | 2 +- posix/process/src/lib.rs | 4 +-- posix/signal/src/lib.rs | 6 ++-- process/kthread/Cargo.toml | 1 + process/kthread/src/process_state.rs | 11 +++++++ util/unittest_support/Cargo.toml | 2 +- util/unittest_support/src/lib.rs | 2 +- 24 files changed, 205 insertions(+), 151 deletions(-) diff --git a/core/kcore/Cargo.toml b/core/kcore/Cargo.toml index 8d9d5d2f..23319ffc 100644 --- a/core/kcore/Cargo.toml +++ b/core/kcore/Cargo.toml @@ -10,6 +10,7 @@ repository.workspace = true [dependencies] backtrace.workspace = true +kaddr_layout.workspace = true kerrno.workspace = true fs-ng-vfs.workspace = true kfs.workspace = true diff --git a/core/kcore/src/config/aarch64.rs b/core/kcore/src/config/aarch64.rs index 33748526..9a1cb465 100644 --- a/core/kcore/src/config/aarch64.rs +++ b/core/kcore/src/config/aarch64.rs @@ -2,36 +2,7 @@ // Copyright 2025 KylinSoft Co., Ltd. // See LICENSES for license details. -//! Architecture-specific memory layout for aarch64. +//! Architecture-specific configuration for aarch64. -/// Kernel layout. /// The size of the kernel stack. pub const KERNEL_STACK_SIZE: usize = 0x4_0000; - -/// User space layout. -/// The base address of the user space. -pub const USER_SPACE_BASE: usize = 0x1000; -/// The size of the user space. -pub const USER_SPACE_SIZE: usize = 0x7fff_ffff_f000; - -/// User interpreter layout. -/// The base address for user interpreter. -pub const USER_INTERP_BASE: usize = 0x400_0000; - -/// User heap layout. -/// The lowest address of the user heap. -pub const USER_HEAP_BASE: usize = 0x4000_0000; -/// The size of the user heap. -pub const USER_HEAP_SIZE: usize = 0x1_0000; -/// The maximum size of the user heap (for brk expansion). -pub const USER_HEAP_SIZE_MAX: usize = 0x2000_0000; - -/// Signal handling layout. -/// The address of signal trampoline (placed at top of user heap). -pub const SIGNAL_TRAMPOLINE: usize = 0x6000_1000; - -/// User stack layout. -/// The highest address of the user stack. -pub const USER_STACK_TOP: usize = 0x7fff_0000_0000; -/// The size of the user stack. -pub const USER_STACK_SIZE: usize = 0x8_0000; diff --git a/core/kcore/src/config/loongarch64.rs b/core/kcore/src/config/loongarch64.rs index 8bfdf40d..7b8dc3b5 100644 --- a/core/kcore/src/config/loongarch64.rs +++ b/core/kcore/src/config/loongarch64.rs @@ -2,36 +2,7 @@ // Copyright 2025 KylinSoft Co., Ltd. // See LICENSES for license details. -//! Architecture-specific memory layout for loongarch64. +//! Architecture-specific configuration for loongarch64. -/// Kernel layout. /// The size of the kernel stack. pub const KERNEL_STACK_SIZE: usize = 0x4_0000; - -/// User space layout. -/// The base address of the user space. -pub const USER_SPACE_BASE: usize = 0x1000; -/// The size of the user space. -pub const USER_SPACE_SIZE: usize = 0x3f_ffff_f000; - -/// User interpreter layout. -/// The base address for user interpreter. -pub const USER_INTERP_BASE: usize = 0x400_0000; - -/// User heap layout. -/// The lowest address of the user heap. -pub const USER_HEAP_BASE: usize = 0x4000_0000; -/// The size of the user heap. -pub const USER_HEAP_SIZE: usize = 0x1_0000; -/// The maximum size of the user heap (for brk expansion). -pub const USER_HEAP_SIZE_MAX: usize = 0x2000_0000; - -/// Signal handling layout. -/// The address of signal trampoline (placed at top of user heap). -pub const SIGNAL_TRAMPOLINE: usize = 0x6000_1000; - -/// User stack layout. -/// The highest address of the user stack. -pub const USER_STACK_TOP: usize = 0x4_0000_0000; -/// The size of the user stack. -pub const USER_STACK_SIZE: usize = 0x8_0000; diff --git a/core/kcore/src/config/mod.rs b/core/kcore/src/config/mod.rs index 18941888..1c2f4f63 100644 --- a/core/kcore/src/config/mod.rs +++ b/core/kcore/src/config/mod.rs @@ -2,6 +2,10 @@ // Copyright 2025 KylinSoft Co., Ltd. // See LICENSES for license details. +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2025 Kylin Soft Co., Ltd. +// See LICENSES for license details. + //! Architecture-specific configurations. cfg_if::cfg_if! { @@ -26,6 +30,12 @@ cfg_if::cfg_if! { } } +// Re-export user-space layout constants from kaddr_layout. +pub use kaddr_layout::{ + SIGNAL_TRAMPOLINE, USER_HEAP_BASE, USER_HEAP_SIZE, USER_HEAP_SIZE_MAX, USER_INTERP_BASE, + USER_SPACE_BASE, USER_SPACE_SIZE, USER_STACK_SIZE, USER_STACK_TOP, +}; + /// Unit tests. #[cfg(unittest)] pub mod tests_config { diff --git a/core/kcore/src/config/riscv64.rs b/core/kcore/src/config/riscv64.rs index dc8c083f..5d96f736 100644 --- a/core/kcore/src/config/riscv64.rs +++ b/core/kcore/src/config/riscv64.rs @@ -2,36 +2,11 @@ // Copyright 2025 KylinSoft Co., Ltd. // See LICENSES for license details. -//! Architecture-specific memory layout for riscv64. +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2025 Kylin Soft Co., Ltd. +// See LICENSES for license details. + +//! Architecture-specific configuration for riscv64. -/// Kernel layout. /// The size of the kernel stack. pub const KERNEL_STACK_SIZE: usize = 0x4_0000; - -/// User space layout. -/// The base address of the user space. -pub const USER_SPACE_BASE: usize = 0x1000; -/// The size of the user space. -pub const USER_SPACE_SIZE: usize = 0x3f_ffff_f000; - -/// User interpreter layout. -/// The base address for user interpreter. -pub const USER_INTERP_BASE: usize = 0x400_0000; - -/// User heap layout. -/// The lowest address of the user heap. -pub const USER_HEAP_BASE: usize = 0x4000_0000; -/// The size of the user heap. -pub const USER_HEAP_SIZE: usize = 0x1_0000; -/// The maximum size of the user heap (for brk expansion). -pub const USER_HEAP_SIZE_MAX: usize = 0x2000_0000; - -/// Signal handling layout. -/// The address of signal trampoline (placed at top of user heap). -pub const SIGNAL_TRAMPOLINE: usize = 0x6000_1000; - -/// User stack layout. -/// The highest address of the user stack. -pub const USER_STACK_TOP: usize = 0x4_0000_0000; -/// The size of the user stack. -pub const USER_STACK_SIZE: usize = 0x8_0000; diff --git a/core/kcore/src/config/x86_64.rs b/core/kcore/src/config/x86_64.rs index 4f642fc6..484914de 100644 --- a/core/kcore/src/config/x86_64.rs +++ b/core/kcore/src/config/x86_64.rs @@ -2,36 +2,7 @@ // Copyright 2025 KylinSoft Co., Ltd. // See LICENSES for license details. -//! Architecture-specific memory layout for x86_64. +//! Architecture-specific configuration for x86_64. -/// Kernel layout. /// The size of the kernel stack. pub const KERNEL_STACK_SIZE: usize = 0x4_0000; - -/// User space layout. -/// The base address of the user space. -pub const USER_SPACE_BASE: usize = 0x1000; -/// The size of the user space. -pub const USER_SPACE_SIZE: usize = 0x7fff_ffff_f000; - -/// User interpreter layout. -/// The base address for user interpreter. -pub const USER_INTERP_BASE: usize = 0x400_0000; - -/// User heap layout. -/// The lowest address of the user heap. -pub const USER_HEAP_BASE: usize = 0x4000_0000; -/// The size of the user heap. -pub const USER_HEAP_SIZE: usize = 0x1_0000; -/// The maximum size of the user heap (for brk expansion). -pub const USER_HEAP_SIZE_MAX: usize = 0x2000_0000; - -/// Signal handling layout. -/// The address of signal trampoline (placed at top of user heap). -pub const SIGNAL_TRAMPOLINE: usize = 0x6000_1000; - -/// User stack layout. -/// The highest address of the user stack. -pub const USER_STACK_TOP: usize = 0x7fff_0000_0000; -/// The size of the user stack. -pub const USER_STACK_SIZE: usize = 0x8_0000; diff --git a/core/kservices/src/unittest_task.rs b/core/kservices/src/unittest_task.rs index 28cb5bb0..378d2bdb 100644 --- a/core/kservices/src/unittest_task.rs +++ b/core/kservices/src/unittest_task.rs @@ -62,11 +62,7 @@ impl InstalledTestThread { Arc::new(SpinNoIrq::new(SignalActions::default())), None, Credentials::root(), - ProcessStateConfig { - user_heap_base: kcore::config::USER_HEAP_BASE, - user_stack_size: kcore::config::USER_STACK_SIZE, - signal_trampoline: kcore::config::SIGNAL_TRAMPOLINE, - }, + ProcessStateConfig::default(), ); let thr = Thread::new(tid, proc_state); init_thread(&thr); diff --git a/core/ksyscall/src/dispatch.rs b/core/ksyscall/src/dispatch.rs index d4977540..f4b90ec3 100644 --- a/core/ksyscall/src/dispatch.rs +++ b/core/ksyscall/src/dispatch.rs @@ -376,7 +376,7 @@ pub fn dispatch_irq_syscall(uctx: &mut UserContext) { Sysno::getpid => sys_getpid(), Sysno::getppid => sys_getppid(), Sysno::gettid => sys_gettid(), - Sysno::getrusage => sys_getrusage(uctx.arg0() as _, uctx.arg1() as _), + Sysno::getrusage => sys_getrusage(uctx.arg0() as _, uctx.arg1().into()), // task sched Sysno::sched_yield => sys_sched_yield(), @@ -467,7 +467,7 @@ pub fn dispatch_irq_syscall(uctx: &mut UserContext) { uctx.arg2() as _, uctx.arg3() as _, ), - Sysno::rt_sigpending => sys_rt_sigpending(uctx.arg0() as _, uctx.arg1() as _), + Sysno::rt_sigpending => sys_rt_sigpending(uctx.arg0().into(), uctx.arg1() as _), Sysno::rt_sigreturn => sys_rt_sigreturn(uctx), Sysno::rt_sigtimedwait => sys_rt_sigtimedwait( uctx, @@ -493,7 +493,7 @@ pub fn dispatch_irq_syscall(uctx: &mut UserContext) { uctx.arg3() as _, uctx.arg4() as _, ), - Sysno::sigaltstack => sys_sigaltstack(uctx.arg0() as _, uctx.arg1() as _), + Sysno::sigaltstack => sys_sigaltstack(uctx.arg0().into(), uctx.arg1().into()), Sysno::futex => sys_futex( uctx.arg0().into(), uctx.arg1() as _, diff --git a/core/ksyscall/src/task/clone.rs b/core/ksyscall/src/task/clone.rs index f8adf139..f15edbef 100644 --- a/core/ksyscall/src/task/clone.rs +++ b/core/ksyscall/src/task/clone.rs @@ -272,11 +272,7 @@ impl CloneRequest { signal_actions, exit_signal, old_proc_data.credentials.read().clone(), - ProcessStateConfig { - user_heap_base: kcore::config::USER_HEAP_BASE, - user_stack_size: kcore::config::USER_STACK_SIZE, - signal_trampoline: kcore::config::SIGNAL_TRAMPOLINE, - }, + ProcessStateConfig::default(), ); proc_state.set_umask(old_proc_data.umask()); // Inherit heap pointers from parent to ensure child's heap state is consistent after fork diff --git a/entry/src/entry.rs b/entry/src/entry.rs index 277a12a1..1b4d6764 100644 --- a/entry/src/entry.rs +++ b/entry/src/entry.rs @@ -59,11 +59,7 @@ pub fn run_initproc(args: &[String], envs: &[String]) -> i32 { Arc::default(), None, Credentials::root(), - ProcessStateConfig { - user_heap_base: kcore::config::USER_HEAP_BASE, - user_stack_size: kcore::config::USER_STACK_SIZE, - signal_trampoline: kcore::config::SIGNAL_TRAMPOLINE, - }, + ProcessStateConfig::default(), ); { kservices::file::add_stdio(&mut proc_state.resources.fd_table().write()) diff --git a/mm/kaddr_layout/src/aarch64.rs b/mm/kaddr_layout/src/aarch64.rs index 41c7e8df..451aff0b 100644 --- a/mm/kaddr_layout/src/aarch64.rs +++ b/mm/kaddr_layout/src/aarch64.rs @@ -2,7 +2,7 @@ // Copyright 2025 KylinSoft Co., Ltd. // See LICENSES for license details. -use crate::LayoutConsts; +use crate::{LayoutConsts, UserLayoutConsts}; // AArch64 kernel VA layout (48-bit TTBR1 half): // @@ -22,3 +22,27 @@ pub const LAYOUT: LayoutConsts = LayoutConsts { kimage_vaddr: 0xffff_8000_0000_0000, kimage_vsize: 0x0000_0000_2000_0000, }; + +// AArch64 user VA layout (TTBR0, 48-bit low half): +// +// 0x0000_0000_0000_1000 ┄┐ ← USER_SPACE_BASE +// 0x0000_0000_0400_0000 ← USER_INTERP_BASE +// 0x0000_0000_4000_0000 ┄┐ ← USER_HEAP_BASE +// │ grows up to USER_HEAP_SIZE_MAX (512 MiB) +// 0x0000_0000_6000_1000 ┄┄┘ ← SIGNAL_TRAMPOLINE +// · +// 0x0000_7FFF_0000_0000 ┄┐ ← USER_STACK_TOP +// ↑ 0x8_0000 │ USER_STACK_SIZE = 512 KiB +// 0x0000_7FFF_FFFF_F000 ┄┄┘ ← USER_SPACE_BASE + USER_SPACE_SIZE + +pub const USER_LAYOUT: UserLayoutConsts = UserLayoutConsts { + user_space_base: 0x1000, + user_space_size: 0x7fff_ffff_f000, + user_interp_base: 0x400_0000, + user_heap_base: 0x4000_0000, + user_heap_size: 0x1_0000, + user_heap_size_max: 0x2000_0000, + signal_trampoline: 0x6000_1000, + user_stack_top: 0x7fff_0000_0000, + user_stack_size: 0x8_0000, +}; diff --git a/mm/kaddr_layout/src/fallback.rs b/mm/kaddr_layout/src/fallback.rs index 33c3774e..7d3158ff 100644 --- a/mm/kaddr_layout/src/fallback.rs +++ b/mm/kaddr_layout/src/fallback.rs @@ -2,7 +2,7 @@ // Copyright 2025 KylinSoft Co., Ltd. // See LICENSES for license details. -use crate::LayoutConsts; +use crate::{LayoutConsts, UserLayoutConsts}; // Generic fallback layout used only for architectures that do not yet have a // dedicated address-space description in this crate. @@ -18,3 +18,15 @@ pub const LAYOUT: LayoutConsts = LayoutConsts { kimage_vaddr: 0xffff_8000_0000_0000, kimage_vsize: 0x0000_0000_2000_0000, }; + +pub const USER_LAYOUT: UserLayoutConsts = UserLayoutConsts { + user_space_base: 0x1000, + user_space_size: 0x7fff_ffff_f000, + user_interp_base: 0x400_0000, + user_heap_base: 0x4000_0000, + user_heap_size: 0x1_0000, + user_heap_size_max: 0x2000_0000, + signal_trampoline: 0x6000_1000, + user_stack_top: 0x7fff_0000_0000, + user_stack_size: 0x8_0000, +}; diff --git a/mm/kaddr_layout/src/lib.rs b/mm/kaddr_layout/src/lib.rs index 33d4450a..59e32cff 100644 --- a/mm/kaddr_layout/src/lib.rs +++ b/mm/kaddr_layout/src/lib.rs @@ -26,6 +26,20 @@ pub struct LayoutConsts { pub kimage_vsize: usize, } +/// User-space virtual memory layout constants (per-architecture). +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct UserLayoutConsts { + pub user_space_base: usize, + pub user_space_size: usize, + pub user_interp_base: usize, + pub user_heap_base: usize, + pub user_heap_size: usize, + pub user_heap_size_max: usize, + pub signal_trampoline: usize, + pub user_stack_top: usize, + pub user_stack_size: usize, +} + pub fn for_arch(arch: &str) -> LayoutConsts { match arch { "aarch64" => aarch64::LAYOUT, @@ -56,6 +70,25 @@ const CURRENT_LAYOUT: LayoutConsts = loongarch64::LAYOUT; )))] const CURRENT_LAYOUT: LayoutConsts = fallback::LAYOUT; +#[cfg(target_arch = "aarch64")] +const CURRENT_USER_LAYOUT: UserLayoutConsts = aarch64::USER_LAYOUT; +#[cfg(target_arch = "x86_64")] +const CURRENT_USER_LAYOUT: UserLayoutConsts = x86_64::USER_LAYOUT; +#[cfg(target_arch = "riscv32")] +const CURRENT_USER_LAYOUT: UserLayoutConsts = fallback::USER_LAYOUT; +#[cfg(target_arch = "riscv64")] +const CURRENT_USER_LAYOUT: UserLayoutConsts = riscv64::USER_LAYOUT; +#[cfg(target_arch = "loongarch64")] +const CURRENT_USER_LAYOUT: UserLayoutConsts = loongarch64::USER_LAYOUT; +#[cfg(not(any( + target_arch = "aarch64", + target_arch = "x86_64", + target_arch = "riscv32", + target_arch = "riscv64", + target_arch = "loongarch64" +)))] +const CURRENT_USER_LAYOUT: UserLayoutConsts = fallback::USER_LAYOUT; + pub const PG_VA_BITS: usize = CURRENT_LAYOUT.pg_va_bits; pub const KERNEL_ASPACE_BASE: usize = CURRENT_LAYOUT.kernel_aspace_base; pub const KERNEL_ASPACE_SIZE: usize = CURRENT_LAYOUT.kernel_aspace_size; @@ -68,6 +101,17 @@ pub const KIMAGE_VADDR: usize = CURRENT_LAYOUT.kimage_vaddr; pub const KIMAGE_VSIZE: usize = CURRENT_LAYOUT.kimage_vsize; pub const BOOT_IO_VADDR: usize = IOMAP_VADDR; pub const BOOT_IO_VSIZE: usize = IOMAP_VSIZE; + +// User-space layout constants. +pub const USER_SPACE_BASE: usize = CURRENT_USER_LAYOUT.user_space_base; +pub const USER_SPACE_SIZE: usize = CURRENT_USER_LAYOUT.user_space_size; +pub const USER_INTERP_BASE: usize = CURRENT_USER_LAYOUT.user_interp_base; +pub const USER_HEAP_BASE: usize = CURRENT_USER_LAYOUT.user_heap_base; +pub const USER_HEAP_SIZE: usize = CURRENT_USER_LAYOUT.user_heap_size; +pub const USER_HEAP_SIZE_MAX: usize = CURRENT_USER_LAYOUT.user_heap_size_max; +pub const SIGNAL_TRAMPOLINE: usize = CURRENT_USER_LAYOUT.signal_trampoline; +pub const USER_STACK_TOP: usize = CURRENT_USER_LAYOUT.user_stack_top; +pub const USER_STACK_SIZE: usize = CURRENT_USER_LAYOUT.user_stack_size; /// Boot-only MMIO slots are sized to a 2 MiB block so the early page-table /// layout can reserve devices on a coarse, architecture-friendly boundary /// without encoding full physical addresses into virtual addresses. diff --git a/mm/kaddr_layout/src/loongarch64.rs b/mm/kaddr_layout/src/loongarch64.rs index da379f69..59ba03c0 100644 --- a/mm/kaddr_layout/src/loongarch64.rs +++ b/mm/kaddr_layout/src/loongarch64.rs @@ -2,7 +2,7 @@ // Copyright 2025 KylinSoft Co., Ltd. // See LICENSES for license details. -use crate::LayoutConsts; +use crate::{LayoutConsts, UserLayoutConsts}; // LoongArch64 kernel VA layout (48-bit canonical upper-half). // @@ -27,3 +27,27 @@ pub const LAYOUT: LayoutConsts = LayoutConsts { kimage_vaddr: 0xffff_ff80_0020_0000, kimage_vsize: 0x0000_0000_2000_0000, }; + +// LoongArch64 user VA layout (low canonical half): +// +// 0x0000_0000_0000_1000 ┄┐ ← USER_SPACE_BASE +// 0x0000_0000_0400_0000 ← USER_INTERP_BASE +// 0x0000_0000_4000_0000 ┄┐ ← USER_HEAP_BASE +// │ grows up to USER_HEAP_SIZE_MAX (512 MiB) +// 0x0000_0000_6000_1000 ┄┄┘ ← SIGNAL_TRAMPOLINE +// · +// 0x0000_0000_4_0000_0000┄┐ ← USER_STACK_TOP +// ↑ 0x8_0000 │ USER_STACK_SIZE = 512 KiB +// 0x0000_003F_FFFF_F000 ┄┄┘ ← USER_SPACE_BASE + USER_SPACE_SIZE + +pub const USER_LAYOUT: UserLayoutConsts = UserLayoutConsts { + user_space_base: 0x1000, + user_space_size: 0x3f_ffff_f000, + user_interp_base: 0x400_0000, + user_heap_base: 0x4000_0000, + user_heap_size: 0x1_0000, + user_heap_size_max: 0x2000_0000, + signal_trampoline: 0x6000_1000, + user_stack_top: 0x4_0000_0000, + user_stack_size: 0x8_0000, +}; diff --git a/mm/kaddr_layout/src/riscv64.rs b/mm/kaddr_layout/src/riscv64.rs index bd9a08a4..8d0eda69 100644 --- a/mm/kaddr_layout/src/riscv64.rs +++ b/mm/kaddr_layout/src/riscv64.rs @@ -2,7 +2,7 @@ // Copyright 2025 KylinSoft Co., Ltd. // See LICENSES for license details. -use crate::LayoutConsts; +use crate::{LayoutConsts, UserLayoutConsts}; // RISC-V Sv39 kernel VA layout: // @@ -25,3 +25,27 @@ pub const LAYOUT: LayoutConsts = LayoutConsts { kimage_vaddr: 0xffff_ffe0_0000_0000, kimage_vsize: 0x0000_0000_2000_0000, }; + +// RISC-V Sv39 user VA layout (low half, 38-bit): +// +// 0x0000_0000_0000_1000 ┄┐ ← USER_SPACE_BASE +// 0x0000_0000_0400_0000 ← USER_INTERP_BASE +// 0x0000_0000_4000_0000 ┄┐ ← USER_HEAP_BASE +// │ grows up to USER_HEAP_SIZE_MAX (512 MiB) +// 0x0000_0000_6000_1000 ┄┄┘ ← SIGNAL_TRAMPOLINE +// · +// 0x0000_0000_4_0000_0000┄┐ ← USER_STACK_TOP +// ↑ 0x8_0000 │ USER_STACK_SIZE = 512 KiB +// 0x0000_003F_FFFF_F000 ┄┄┘ ← USER_SPACE_BASE + USER_SPACE_SIZE + +pub const USER_LAYOUT: UserLayoutConsts = UserLayoutConsts { + user_space_base: 0x1000, + user_space_size: 0x3f_ffff_f000, + user_interp_base: 0x400_0000, + user_heap_base: 0x4000_0000, + user_heap_size: 0x1_0000, + user_heap_size_max: 0x2000_0000, + signal_trampoline: 0x6000_1000, + user_stack_top: 0x4_0000_0000, + user_stack_size: 0x8_0000, +}; diff --git a/mm/kaddr_layout/src/x86_64.rs b/mm/kaddr_layout/src/x86_64.rs index 26dcd49e..c8379040 100644 --- a/mm/kaddr_layout/src/x86_64.rs +++ b/mm/kaddr_layout/src/x86_64.rs @@ -2,7 +2,7 @@ // Copyright 2025 KylinSoft Co., Ltd. // See LICENSES for license details. -use crate::LayoutConsts; +use crate::{LayoutConsts, UserLayoutConsts}; // x86_64 kernel VA layout: // @@ -24,3 +24,29 @@ pub const LAYOUT: LayoutConsts = LayoutConsts { kimage_vaddr: 0xffff_ff80_0000_0000, kimage_vsize: 0x0000_0080_0000_0000, }; + +// x86_64 user VA layout (low canonical half): +// +// 0x0000_0000_0000_0000 ┄┄┐ (null guard) +// 0x0000_0000_0000_1000 ┄┄┘ +// 0x0000_0000_0400_0000 ← USER_INTERP_BASE (ELF interpreter) +// 0x0000_0000_4000_0000 ┄┐ ← USER_HEAP_BASE (brk starts here) +// │ initial 64 KiB, can grow to 512 MiB +// 0x0000_0000_6000_1000 ┄┄┘ ← SIGNAL_TRAMPOLINE +// · +// · (gap) +// · +// 0x0000_7FFF_0000_0000 ┄┐ ← USER_STACK_TOP +// ↑ 0x8_0000 │ USER_STACK_SIZE = 512 KiB +// 0x0000_7FFF_FFFF_F000 ┄┄┘ ← USER_SPACE_BASE + USER_SPACE_SIZE +pub const USER_LAYOUT: UserLayoutConsts = UserLayoutConsts { + user_space_base: 0x1000, + user_space_size: 0x7fff_ffff_f000, + user_interp_base: 0x400_0000, + user_heap_base: 0x4000_0000, + user_heap_size: 0x1_0000, + user_heap_size_max: 0x2000_0000, + signal_trampoline: 0x6000_1000, + user_stack_top: 0x7fff_0000_0000, + user_stack_size: 0x8_0000, +}; diff --git a/posix/mm/Cargo.toml b/posix/mm/Cargo.toml index 75bd6c40..8b6925d1 100644 --- a/posix/mm/Cargo.toml +++ b/posix/mm/Cargo.toml @@ -10,6 +10,7 @@ repository.workspace = true documentation.workspace = true [dependencies] +kaddr_layout = { workspace = true } kcore = { workspace = true } kerrno = { workspace = true } kfs = { workspace = true } diff --git a/posix/mm/src/brk.rs b/posix/mm/src/brk.rs index 8e9dac39..ff6f678a 100644 --- a/posix/mm/src/brk.rs +++ b/posix/mm/src/brk.rs @@ -4,7 +4,7 @@ //! Heap management syscalls. -use kcore::config::{USER_HEAP_BASE, USER_HEAP_SIZE, USER_HEAP_SIZE_MAX}; +use kaddr_layout::{USER_HEAP_BASE, USER_HEAP_SIZE, USER_HEAP_SIZE_MAX}; use kerrno::KResult; use khal::paging::{MappingFlags, PageSize}; use kthread::current_process_state; diff --git a/posix/process/src/lib.rs b/posix/process/src/lib.rs index 5132f6aa..082a6358 100644 --- a/posix/process/src/lib.rs +++ b/posix/process/src/lib.rs @@ -24,7 +24,7 @@ use ktask::current; use kthread::{AsThread, ProcessState, Thread, get_process_group, get_process_state, get_task}; use linux_raw_sys::general::{__kernel_old_timeval, rusage}; use osvm::VirtMutPtr; -use posix_types::TimeValueLike; +use posix_types::{TimeValueLike, UserPtr}; /// Returns the process ID of the current process. pub fn sys_getpid() -> KResult { @@ -174,7 +174,7 @@ fn children_rusage(proc_state: &ProcessState) -> Rusage { } /// Returns resource usage information for the current process, its children, or the current thread. -pub fn sys_getrusage(who: i32, usage: *mut rusage) -> KResult { +pub fn sys_getrusage(who: i32, usage: UserPtr) -> KResult { const RUSAGE_SELF: i32 = linux_raw_sys::general::RUSAGE_SELF as i32; const RUSAGE_CHILDREN: i32 = linux_raw_sys::general::RUSAGE_CHILDREN; const RUSAGE_THREAD: i32 = linux_raw_sys::general::RUSAGE_THREAD as i32; diff --git a/posix/signal/src/lib.rs b/posix/signal/src/lib.rs index 3ffae20a..7fa74df9 100644 --- a/posix/signal/src/lib.rs +++ b/posix/signal/src/lib.rs @@ -39,7 +39,7 @@ use linux_raw_sys::general::{ timespec, }; use osvm::{VirtMutPtr, VirtPtr}; -use posix_types::TimeValueLike; +use posix_types::{TimeValueLike, UserConstPtr, UserPtr}; /// Validates that the signal set size matches the expected size. pub fn check_sigset_size(size: usize) -> KResult<()> { @@ -125,7 +125,7 @@ pub fn sys_rt_sigaction( /// Returns the set of pending signals. /// /// See . -pub fn sys_rt_sigpending(set: *mut SignalSet, sigsetsize: usize) -> KResult { +pub fn sys_rt_sigpending(set: UserPtr, sigsetsize: usize) -> KResult { check_sigset_size(sigsetsize)?; set.write_vm(kthread::current_thread().signal.pending())?; Ok(0) @@ -346,7 +346,7 @@ pub fn sys_rt_sigsuspend( /// Sets or retrieves the alternate signal stack. /// /// See . -pub fn sys_sigaltstack(ss: *const SignalStack, old_ss: *mut SignalStack) -> KResult { +pub fn sys_sigaltstack(ss: UserConstPtr, old_ss: UserPtr) -> KResult { let signal = &kthread::current_thread().signal; if let Some(old_ss) = old_ss.check_non_null() { diff --git a/process/kthread/Cargo.toml b/process/kthread/Cargo.toml index 95fc01cf..05f3ce2c 100644 --- a/process/kthread/Cargo.toml +++ b/process/kthread/Cargo.toml @@ -16,6 +16,7 @@ tee_ta_sign = [] extern-trait.workspace = true hashbrown.workspace = true kcred.workspace = true +kaddr_layout.workspace = true kerrno.workspace = true klogger.workspace = true kpoll.workspace = true diff --git a/process/kthread/src/process_state.rs b/process/kthread/src/process_state.rs index 71646bf2..efb71589 100644 --- a/process/kthread/src/process_state.rs +++ b/process/kthread/src/process_state.rs @@ -36,6 +36,17 @@ pub struct ProcessStateConfig { pub signal_trampoline: usize, } +impl Default for ProcessStateConfig { + fn default() -> Self { + use kaddr_layout::{SIGNAL_TRAMPOLINE, USER_HEAP_BASE, USER_STACK_SIZE}; + Self { + user_heap_base: USER_HEAP_BASE, + user_stack_size: USER_STACK_SIZE, + signal_trampoline: SIGNAL_TRAMPOLINE, + } + } +} + /// [`Process`]-shared state. pub struct ProcessState { /// The process. diff --git a/util/unittest_support/Cargo.toml b/util/unittest_support/Cargo.toml index cfc5675a..8864e31b 100644 --- a/util/unittest_support/Cargo.toml +++ b/util/unittest_support/Cargo.toml @@ -10,8 +10,8 @@ repository.workspace = true documentation.workspace = true [dependencies] +kaddr_layout.workspace = true kalloc.workspace = true -kcore.workspace = true kerrno.workspace = true khal.workspace = true kthread.workspace = true diff --git a/util/unittest_support/src/lib.rs b/util/unittest_support/src/lib.rs index 220714fd..f032c84f 100644 --- a/util/unittest_support/src/lib.rs +++ b/util/unittest_support/src/lib.rs @@ -23,7 +23,7 @@ use kthread::AsThread; use memaddr::{PAGE_SIZE_4K, VirtAddr}; use osvm::{read_vm_mem, write_vm_mem}; -static NEXT_TEST_USER_ADDR: AtomicUsize = AtomicUsize::new(kcore::config::USER_HEAP_BASE); +static NEXT_TEST_USER_ADDR: AtomicUsize = AtomicUsize::new(kaddr_layout::USER_HEAP_BASE); #[macro_export] macro_rules! __unittest_support_user_vec { -- Gitee From 23b0f733e81e3e31b9189b9d69117352ab39c9c0 Mon Sep 17 00:00:00 2001 From: Weikang Guo Date: Thu, 7 May 2026 20:38:58 +0800 Subject: [PATCH 08/21] fix clippy Signed-off-by: Weikang Guo --- core/kservices/src/vfs/dev/dice.rs | 2 -- mm/kaddr_layout/src/lib.rs | 11 +++++++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/core/kservices/src/vfs/dev/dice.rs b/core/kservices/src/vfs/dev/dice.rs index 3bb4a060..8b676b36 100644 --- a/core/kservices/src/vfs/dev/dice.rs +++ b/core/kservices/src/vfs/dev/dice.rs @@ -90,8 +90,6 @@ fn get_process_hash() -> KResult> { use alloc::format; use kfs::FS_CONTEXT; - use ktask::current; - use kthread::AsThread; use mbedtls::hash::{Md, Type}; let pid = kthread::current_thread().pid(); diff --git a/mm/kaddr_layout/src/lib.rs b/mm/kaddr_layout/src/lib.rs index 59e32cff..4790785a 100644 --- a/mm/kaddr_layout/src/lib.rs +++ b/mm/kaddr_layout/src/lib.rs @@ -51,6 +51,17 @@ pub fn for_arch(arch: &str) -> LayoutConsts { } } +pub fn user_layout_for_arch(arch: &str) -> UserLayoutConsts { + match arch { + "aarch64" => aarch64::USER_LAYOUT, + "riscv64" => riscv64::USER_LAYOUT, + "x86_64" => x86_64::USER_LAYOUT, + "riscv32" => fallback::USER_LAYOUT, + "loongarch64" => loongarch64::USER_LAYOUT, + _ => fallback::USER_LAYOUT, + } +} + #[cfg(target_arch = "aarch64")] const CURRENT_LAYOUT: LayoutConsts = aarch64::LAYOUT; #[cfg(target_arch = "x86_64")] -- Gitee From 18526de6a5d1886dc647004b27a9b83c1c09baea Mon Sep 17 00:00:00 2001 From: Weikang Guo Date: Thu, 7 May 2026 20:40:50 +0800 Subject: [PATCH 09/21] fix fmt Signed-off-by: Weikang Guo --- core/kcore/src/mm.rs | 2 +- core/ksyscall/src/fs/event.rs | 8 +++++++- core/ksyscall/src/fs/pidfd.rs | 3 ++- core/ksyscall/src/fs/signalfd.rs | 8 +++++++- core/ksyscall/src/io_mpx/epoll.rs | 6 +++++- core/ksyscall/src/net/cmsg.rs | 5 +---- core/ksyscall/src/net/io.rs | 6 +++++- core/ksyscall/src/task/wait.rs | 8 ++++---- posix/credentials/src/helpers.rs | 2 +- posix/fs/src/io.rs | 4 +++- posix/signal/src/lib.rs | 5 ++++- 11 files changed, 40 insertions(+), 17 deletions(-) diff --git a/core/kcore/src/mm.rs b/core/kcore/src/mm.rs index 90a80179..f4eeb383 100644 --- a/core/kcore/src/mm.rs +++ b/core/kcore/src/mm.rs @@ -20,6 +20,7 @@ use khal::{ use kspin::IrqSave; use ksync::Mutex; use ktask::current; +use kthread::AsThread; use memaddr::{MemoryAddr, PAGE_SIZE_4K, VirtAddr}; use memspace::AddrSpace; use memspace_file::{new_alloc, new_cow}; @@ -30,7 +31,6 @@ use crate::{ config::{USER_SPACE_BASE, USER_SPACE_SIZE}, lrucache::LruCache, }; -use kthread::AsThread; /// Creates a new empty user address space. pub fn new_user_aspace_empty() -> KResult { diff --git a/core/ksyscall/src/fs/event.rs b/core/ksyscall/src/fs/event.rs index 38454cd7..dd2b1fd3 100644 --- a/core/ksyscall/src/fs/event.rs +++ b/core/ksyscall/src/fs/event.rs @@ -39,5 +39,11 @@ pub fn sys_eventfd2(initval: u32, flags: u32) -> KResult { let event_fd = EventFd::new(initval as _, flags.contains(EventFdFlags::SEMAPHORE)); event_fd.set_nonblocking(flags.contains(EventFdFlags::NONBLOCK))?; - add_file_like(&fd_table, max_nofile, event_fd as _, flags.contains(EventFdFlags::CLOEXEC)).map(|fd| fd as _) + add_file_like( + &fd_table, + max_nofile, + event_fd as _, + flags.contains(EventFdFlags::CLOEXEC), + ) + .map(|fd| fd as _) } diff --git a/core/ksyscall/src/fs/pidfd.rs b/core/ksyscall/src/fs/pidfd.rs index 81097491..879e135f 100644 --- a/core/ksyscall/src/fs/pidfd.rs +++ b/core/ksyscall/src/fs/pidfd.rs @@ -38,7 +38,8 @@ pub fn sys_pidfd_open(pid: u32, flags: u32) -> KResult { let proc_state = kthread::current_process_state(); let fd_table = proc_state.resources.fd_table(); let max_nofile = proc_state.resources.max_nofile(); - fd.add_to_fd_table(&fd_table, max_nofile, true).map(|fd| fd as _) + fd.add_to_fd_table(&fd_table, max_nofile, true) + .map(|fd| fd as _) } /// Get a duplicate of a file descriptor from another process using its pidfd diff --git a/core/ksyscall/src/fs/signalfd.rs b/core/ksyscall/src/fs/signalfd.rs index 66fbaedc..c5f1b8a2 100644 --- a/core/ksyscall/src/fs/signalfd.rs +++ b/core/ksyscall/src/fs/signalfd.rs @@ -80,5 +80,11 @@ pub fn sys_signalfd4( // Add to file descriptor table let max_nofile = proc_state.resources.max_nofile(); - add_file_like(&fd_table, max_nofile, signalfd as _, flags.contains(SignalfdFlags::CLOEXEC)).map(|fd| fd as _) + add_file_like( + &fd_table, + max_nofile, + signalfd as _, + flags.contains(SignalfdFlags::CLOEXEC), + ) + .map(|fd| fd as _) } diff --git a/core/ksyscall/src/io_mpx/epoll.rs b/core/ksyscall/src/io_mpx/epoll.rs index 05d448a7..116bbe1c 100644 --- a/core/ksyscall/src/io_mpx/epoll.rs +++ b/core/ksyscall/src/io_mpx/epoll.rs @@ -49,7 +49,11 @@ pub fn sys_epoll_create1(flags: u32) -> KResult { let fd_table = proc_state.resources.fd_table(); let max_nofile = proc_state.resources.max_nofile(); Epoll::new() - .add_to_fd_table(&fd_table, max_nofile, flags.contains(EpollCreateFlags::CLOEXEC)) + .add_to_fd_table( + &fd_table, + max_nofile, + flags.contains(EpollCreateFlags::CLOEXEC), + ) .map(|fd| fd as isize) } diff --git a/core/ksyscall/src/net/cmsg.rs b/core/ksyscall/src/net/cmsg.rs index c7fa73c7..e6628b65 100644 --- a/core/ksyscall/src/net/cmsg.rs +++ b/core/ksyscall/src/net/cmsg.rs @@ -120,10 +120,7 @@ pub enum CMsg { } impl CMsg { /// Parse a control message header and extract its data - pub fn parse( - fd_table: &ksync::RwLock, - hdr: &cmsghdr, - ) -> KResult { + pub fn parse(fd_table: &ksync::RwLock, hdr: &cmsghdr) -> KResult { if hdr.cmsg_len < size_of::() { return Err(KError::InvalidInput); } diff --git a/core/ksyscall/src/net/io.rs b/core/ksyscall/src/net/io.rs index e10ffd20..cb976c84 100644 --- a/core/ksyscall/src/net/io.rs +++ b/core/ksyscall/src/net/io.rs @@ -329,7 +329,11 @@ pub fn sys_sendmmsg(fd: i32, msgvec: UserPtr, vlen: u32, flags: u32) -> let fd_table = kthread::current_process_state().resources.fd_table(); let mut sent = 0; for msg in msgvec.iter_mut() { - let cmsg = parse_send_cmsgs(&fd_table, msg.msg_hdr.msg_control as usize, msg.msg_hdr.msg_controllen)?; + let cmsg = parse_send_cmsgs( + &fd_table, + msg.msg_hdr.msg_control as usize, + msg.msg_hdr.msg_controllen, + )?; match send_impl( fd, IoVectorBuf::new(msg.msg_hdr.msg_iov as *const IoVec, msg.msg_hdr.msg_iovlen)? diff --git a/core/ksyscall/src/task/wait.rs b/core/ksyscall/src/task/wait.rs index 70b98ee6..2d4050e9 100644 --- a/core/ksyscall/src/task/wait.rs +++ b/core/ksyscall/src/task/wait.rs @@ -106,10 +106,10 @@ pub fn sys_waitpid(pid: i32, exit_code: *mut i32, options: u32) -> KResult KResult { let proc_state = kthread::current_process_state(); let fd_table = proc_state.resources.fd_table(); let max_nofile = proc_state.resources.max_nofile(); - DummyFd.add_to_fd_table(&fd_table, max_nofile, false).map(|fd| fd as isize) + DummyFd + .add_to_fd_table(&fd_table, max_nofile, false) + .map(|fd| fd as isize) } /// Read data from the file indicated by `fd`. diff --git a/posix/signal/src/lib.rs b/posix/signal/src/lib.rs index 7fa74df9..0dae058a 100644 --- a/posix/signal/src/lib.rs +++ b/posix/signal/src/lib.rs @@ -346,7 +346,10 @@ pub fn sys_rt_sigsuspend( /// Sets or retrieves the alternate signal stack. /// /// See . -pub fn sys_sigaltstack(ss: UserConstPtr, old_ss: UserPtr) -> KResult { +pub fn sys_sigaltstack( + ss: UserConstPtr, + old_ss: UserPtr, +) -> KResult { let signal = &kthread::current_thread().signal; if let Some(old_ss) = old_ss.check_non_null() { -- Gitee From 8ad3e12397458f0a9dbc187cf49cded9815f27d2 Mon Sep 17 00:00:00 2001 From: Weikang Guo Date: Fri, 8 May 2026 10:20:09 +0800 Subject: [PATCH 10/21] fix epoll ut panic Signed-off-by: Weikang Guo --- core/kservices/src/file/epoll.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/core/kservices/src/file/epoll.rs b/core/kservices/src/file/epoll.rs index 6b1dd277..01fa8077 100644 --- a/core/kservices/src/file/epoll.rs +++ b/core/kservices/src/file/epoll.rs @@ -22,7 +22,6 @@ use kfd::FdTable; use kpoll::{IoEvents, PollSet, Pollable}; use kspin::SpinNoPreempt; use ksync::RwLock; -use kthread::current_process_state; use linux_raw_sys::general::{EPOLLET, EPOLLONESHOT, epoll_event}; use crate::file::{FileLike, get_file_like}; @@ -320,7 +319,7 @@ impl Epoll { /// Adds a file descriptor interest to the epoll instance. pub fn add(&self, fd: i32, event: EpollEvent, flags: EpollFlags) -> KResult<()> { - let fd_table = current_process_state().resources.fd_table(); + let fd_table = kthread::current_process_state().resources.fd_table(); let key = EntryKey::new(&fd_table, fd)?; let interest = Arc::new(EpollInterest::new(key.clone(), event, flags)); let mut guard = self.inner.interests.lock(); @@ -336,7 +335,7 @@ impl Epoll { /// Modifies an existing interest for the given file descriptor. pub fn modify(&self, fd: i32, event: EpollEvent, flags: EpollFlags) -> KResult<()> { - let fd_table = current_process_state().resources.fd_table(); + let fd_table = kthread::current_process_state().resources.fd_table(); let key = EntryKey::new(&fd_table, fd)?; let interest = Arc::new(EpollInterest::new(key.clone(), event, flags)); @@ -360,7 +359,7 @@ impl Epoll { /// Removes an existing interest for the given file descriptor. pub fn delete(&self, fd: i32) -> KResult<()> { - let fd_table = current_process_state().resources.fd_table(); + let fd_table = kthread::current_process_state().resources.fd_table(); let key = EntryKey::new(&fd_table, fd)?; self.inner .interests @@ -609,14 +608,14 @@ mod epoll_tests { assert_eq!(result, Err(KError::WouldBlock)); } - #[def_test] + #[def_test(custom)] fn test_epoll_delete_nonexistent() { let epoll = Epoll::new(); let result = epoll.delete(999); assert!(result.is_err()); } - #[def_test] + #[def_test(custom)] fn test_epoll_modify_nonexistent() { let epoll = Epoll::new(); let event = EpollEvent { -- Gitee From de7b206d3f30f2f0d25287da693ae143bbb6b14e Mon Sep 17 00:00:00 2001 From: Weikang Guo Date: Fri, 8 May 2026 10:22:20 +0800 Subject: [PATCH 11/21] remove dup file head Signed-off-by: Weikang Guo --- core/kcore/src/config/mod.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/core/kcore/src/config/mod.rs b/core/kcore/src/config/mod.rs index 1c2f4f63..274cd751 100644 --- a/core/kcore/src/config/mod.rs +++ b/core/kcore/src/config/mod.rs @@ -2,10 +2,6 @@ // Copyright 2025 KylinSoft Co., Ltd. // See LICENSES for license details. -// SPDX-License-Identifier: Apache-2.0 -// Copyright 2025 Kylin Soft Co., Ltd. -// See LICENSES for license details. - //! Architecture-specific configurations. cfg_if::cfg_if! { -- Gitee From a63b63e1b6f36b60d51296a658411430757c737b Mon Sep 17 00:00:00 2001 From: Weikang Guo Date: Fri, 8 May 2026 10:42:10 +0800 Subject: [PATCH 12/21] should not reexport kaddr at kcore Signed-off-by: Weikang Guo --- core/kcore/src/config/mod.rs | 39 -------------------------------- core/kcore/src/config/riscv64.rs | 4 ---- core/kcore/src/mm.rs | 24 +++++++++----------- core/ksyscall/Cargo.toml | 1 + core/ksyscall/src/task/execve.rs | 3 ++- fs/procfs/Cargo.toml | 1 + fs/procfs/src/task.rs | 10 ++++---- mm/kaddr_layout/src/lib.rs | 11 +++++++++ 8 files changed, 30 insertions(+), 63 deletions(-) diff --git a/core/kcore/src/config/mod.rs b/core/kcore/src/config/mod.rs index 274cd751..461f103f 100644 --- a/core/kcore/src/config/mod.rs +++ b/core/kcore/src/config/mod.rs @@ -25,42 +25,3 @@ cfg_if::cfg_if! { compile_error!("Unsupported architecture"); } } - -// Re-export user-space layout constants from kaddr_layout. -pub use kaddr_layout::{ - SIGNAL_TRAMPOLINE, USER_HEAP_BASE, USER_HEAP_SIZE, USER_HEAP_SIZE_MAX, USER_INTERP_BASE, - USER_SPACE_BASE, USER_SPACE_SIZE, USER_STACK_SIZE, USER_STACK_TOP, -}; - -/// Unit tests. -#[cfg(unittest)] -pub mod tests_config { - use unittest::def_test; - - use super::*; - - #[def_test] - fn test_user_space_range() { - assert!(USER_SPACE_SIZE > 0); - assert!(USER_SPACE_BASE < USER_SPACE_BASE + USER_SPACE_SIZE); - } - - #[def_test] - fn test_user_stack_range() { - assert!(USER_STACK_SIZE > 0); - assert!(USER_STACK_TOP > USER_STACK_SIZE); - } - - #[def_test] - fn test_heap_limits() { - assert!(USER_HEAP_SIZE > 0); - assert!(USER_HEAP_SIZE_MAX >= USER_HEAP_SIZE); - } - - #[def_test] - fn test_signal_trampoline_and_heap_base_ordering() { - assert!(SIGNAL_TRAMPOLINE > USER_SPACE_BASE); - assert!(USER_HEAP_BASE >= USER_SPACE_BASE); - assert!(USER_HEAP_BASE + USER_HEAP_SIZE <= USER_SPACE_BASE + USER_SPACE_SIZE); - } -} diff --git a/core/kcore/src/config/riscv64.rs b/core/kcore/src/config/riscv64.rs index 5d96f736..68b75924 100644 --- a/core/kcore/src/config/riscv64.rs +++ b/core/kcore/src/config/riscv64.rs @@ -2,10 +2,6 @@ // Copyright 2025 KylinSoft Co., Ltd. // See LICENSES for license details. -// SPDX-License-Identifier: Apache-2.0 -// Copyright 2025 Kylin Soft Co., Ltd. -// See LICENSES for license details. - //! Architecture-specific configuration for riscv64. /// The size of the kernel stack. diff --git a/core/kcore/src/mm.rs b/core/kcore/src/mm.rs index f4eeb383..181eef49 100644 --- a/core/kcore/src/mm.rs +++ b/core/kcore/src/mm.rs @@ -9,6 +9,7 @@ use core::{ffi::CStr, hint::unlikely, iter, mem::MaybeUninit}; use extern_trait::extern_trait; use fs_ng_vfs::Location; +use kaddr_layout::{USER_SPACE_BASE, USER_SPACE_SIZE}; use kernel_elf_parser::{AuxEntry, ELFHeaders, ELFHeadersBuilder, ELFParser, app_stack_region}; use kerrno::{KError, KResult}; use kfs::{CachedFile, FS_CONTEXT, FileBackend}; @@ -27,16 +28,13 @@ use memspace_file::{new_alloc, new_cow}; use osvm::{MemError, MemResult, VirtMemIo}; use ouroboros::self_referencing; -use crate::{ - config::{USER_SPACE_BASE, USER_SPACE_SIZE}, - lrucache::LruCache, -}; +use crate::lrucache::LruCache; /// Creates a new empty user address space. pub fn new_user_aspace_empty() -> KResult { AddrSpace::new_empty( - VirtAddr::from_usize(crate::config::USER_SPACE_BASE), - crate::config::USER_SPACE_SIZE, + VirtAddr::from_usize(kaddr_layout::USER_SPACE_BASE), + kaddr_layout::USER_SPACE_SIZE, ) } @@ -57,7 +55,7 @@ pub fn copy_from_kernel(_aspace: &mut AddrSpace) -> KResult { pub fn map_trampoline(aspace: &mut AddrSpace) -> KResult { let signal_trampoline_paddr = v2p(ksignal::arch::signal_trampoline_address().into()); aspace.map_linear( - crate::config::SIGNAL_TRAMPOLINE.into(), + kaddr_layout::SIGNAL_TRAMPOLINE.into(), signal_trampoline_paddr, PAGE_SIZE_4K, MappingFlags::READ | MappingFlags::EXECUTE | MappingFlags::USER, @@ -248,9 +246,9 @@ impl ElfLoader { (entry, None) }; - let elf = map_elf(uspace, crate::config::USER_SPACE_BASE, elf)?; + let elf = map_elf(uspace, kaddr_layout::USER_SPACE_BASE, elf)?; let ldso = ldso - .map(|elf| map_elf(uspace, crate::config::USER_INTERP_BASE, elf)) + .map(|elf| map_elf(uspace, kaddr_layout::USER_INTERP_BASE, elf)) .transpose()?; let entry = VirtAddr::from_usize( @@ -326,8 +324,8 @@ pub fn load_user_app( } }; - let ustack_top = VirtAddr::from_usize(crate::config::USER_STACK_TOP); - let ustack_size = crate::config::USER_STACK_SIZE; + let ustack_top = VirtAddr::from_usize(kaddr_layout::USER_STACK_TOP); + let ustack_size = kaddr_layout::USER_STACK_SIZE; let ustack_start = ustack_top - ustack_size; debug!("Mapping user stack: {ustack_start:#x?} -> {ustack_top:#x?}"); @@ -349,8 +347,8 @@ pub fn load_user_app( )?; uspace.write(user_sp, stack_data.as_slice())?; - let heap_start = VirtAddr::from_usize(crate::config::USER_HEAP_BASE); - let heap_size = crate::config::USER_HEAP_SIZE; + let heap_start = VirtAddr::from_usize(kaddr_layout::USER_HEAP_BASE); + let heap_size = kaddr_layout::USER_HEAP_SIZE; uspace.map( heap_start, heap_size, diff --git a/core/ksyscall/Cargo.toml b/core/ksyscall/Cargo.toml index 6580b882..fe36cee1 100644 --- a/core/ksyscall/Cargo.toml +++ b/core/ksyscall/Cargo.toml @@ -18,6 +18,7 @@ tee = [ ] [dependencies] +kaddr_layout.workspace = true kfd.workspace = true ksync.workspace = true kservices = { workspace = true } diff --git a/core/ksyscall/src/task/execve.rs b/core/ksyscall/src/task/execve.rs index 0fee46e3..6a689d91 100644 --- a/core/ksyscall/src/task/execve.rs +++ b/core/ksyscall/src/task/execve.rs @@ -12,7 +12,8 @@ use alloc::{string::ToString, sync::Arc, vec::Vec}; use core::ffi::c_char; -use kcore::{config::USER_HEAP_BASE, mm::load_user_app}; +use kaddr_layout::USER_HEAP_BASE; +use kcore::mm::load_user_app; use kerrno::{KError, KResult}; use kfs::FS_CONTEXT; use khal::uspace::UserContext; diff --git a/fs/procfs/Cargo.toml b/fs/procfs/Cargo.toml index bbdd371c..67592d13 100644 --- a/fs/procfs/Cargo.toml +++ b/fs/procfs/Cargo.toml @@ -13,6 +13,7 @@ tee = ["kcore/tee", "kthread/tee", "dep:tee_task_iface"] [dependencies] fs-ng-vfs.workspace = true +kaddr_layout.workspace = true kalloc.workspace = true kcore.workspace = true kfs.workspace = true diff --git a/fs/procfs/src/task.rs b/fs/procfs/src/task.rs index f0e3a2e5..ea70e2b0 100644 --- a/fs/procfs/src/task.rs +++ b/fs/procfs/src/task.rs @@ -14,14 +14,12 @@ use alloc::{ use core::{ffi::CStr, iter, str}; use fs_ng_vfs::{NodeType, VfsError, VfsResult}; +use kaddr_layout::{SIGNAL_TRAMPOLINE, USER_HEAP_BASE, USER_STACK_SIZE, USER_STACK_TOP}; #[cfg(feature = "tee")] use kcore::vfs::DirMapping; -use kcore::{ - config::{SIGNAL_TRAMPOLINE, USER_HEAP_BASE, USER_STACK_SIZE, USER_STACK_TOP}, - vfs::{ - NodeOpsMux, RwFile, SeqFileNode, SeqIterator, SimpleDir, SimpleDirOps, SimpleFile, - SimpleFileOperation, SimpleFs, - }, +use kcore::vfs::{ + NodeOpsMux, RwFile, SeqFileNode, SeqIterator, SimpleDir, SimpleDirOps, SimpleFile, + SimpleFileOperation, SimpleFs, }; use khal::paging::MappingFlags; use kprocess::Process; diff --git a/mm/kaddr_layout/src/lib.rs b/mm/kaddr_layout/src/lib.rs index 4790785a..ff5ab2ed 100644 --- a/mm/kaddr_layout/src/lib.rs +++ b/mm/kaddr_layout/src/lib.rs @@ -123,6 +123,7 @@ pub const USER_HEAP_SIZE_MAX: usize = CURRENT_USER_LAYOUT.user_heap_size_max; pub const SIGNAL_TRAMPOLINE: usize = CURRENT_USER_LAYOUT.signal_trampoline; pub const USER_STACK_TOP: usize = CURRENT_USER_LAYOUT.user_stack_top; pub const USER_STACK_SIZE: usize = CURRENT_USER_LAYOUT.user_stack_size; + /// Boot-only MMIO slots are sized to a 2 MiB block so the early page-table /// layout can reserve devices on a coarse, architecture-friendly boundary /// without encoding full physical addresses into virtual addresses. @@ -138,6 +139,16 @@ const _: () = assert!(is_power_of_two(BOOT_IO_SLOT_SIZE)); const _: () = assert!(BOOT_IO_VSIZE == 0 || BOOT_IO_SLOT_SIZE <= BOOT_IO_VSIZE); const _: () = assert!(BOOT_IO_VSIZE == 0 || BOOT_UART_SLOT < (BOOT_IO_VSIZE / BOOT_IO_SLOT_SIZE)); +// Compile-time invariants for user-space layout constants. +const _: () = assert!(USER_SPACE_SIZE > 0); +const _: () = assert!(USER_STACK_SIZE > 0); +const _: () = assert!(USER_HEAP_SIZE > 0); +const _: () = assert!(USER_HEAP_SIZE_MAX >= USER_HEAP_SIZE); +const _: () = assert!(USER_STACK_TOP > USER_STACK_SIZE); +const _: () = assert!(SIGNAL_TRAMPOLINE > USER_SPACE_BASE); +const _: () = assert!(USER_HEAP_BASE >= USER_SPACE_BASE); +const _: () = assert!(USER_HEAP_BASE + USER_HEAP_SIZE <= USER_SPACE_BASE + USER_SPACE_SIZE); + /// Runtime offset from physical kernel load address to the linked kernel-image /// virtual address. /// -- Gitee From edb82c32ac9ce604a3b3858c726787392b0cfb9e Mon Sep 17 00:00:00 2001 From: Weikang Guo Date: Fri, 8 May 2026 13:42:01 +0800 Subject: [PATCH 13/21] reconstructor kfd Signed-off-by: Weikang Guo --- core/kservices/src/file/epoll.rs | 17 +- core/kservices/src/file/fs.rs | 32 +-- core/kservices/src/file/mod.rs | 22 +- core/kservices/src/file/net.rs | 6 +- core/kservices/src/task.rs | 2 +- core/kservices/src/vfs/dev/loop.rs | 6 +- core/kservices/src/vfs/mod.rs | 2 +- core/ksyscall/Cargo.toml | 1 - core/ksyscall/src/fs/event.rs | 16 +- core/ksyscall/src/fs/pidfd.rs | 22 +- core/ksyscall/src/fs/signalfd.rs | 19 +- core/ksyscall/src/fs/timerfd.rs | 16 +- core/ksyscall/src/io_mpx/epoll.rs | 23 +- core/ksyscall/src/io_mpx/poll.rs | 3 +- core/ksyscall/src/io_mpx/select.rs | 2 +- core/ksyscall/src/net/cmsg.rs | 4 +- core/ksyscall/src/net/io.rs | 34 +-- core/ksyscall/src/net/name.rs | 11 +- core/ksyscall/src/net/opt.rs | 7 +- core/ksyscall/src/net/socket.rs | 56 +++-- core/ksyscall/src/task/clone.rs | 7 +- core/ksyscall/src/task/execve.rs | 11 +- posix/fs/src/ctl.rs | 10 +- posix/fs/src/fd_ops.rs | 85 ++------ posix/fs/src/io.rs | 98 ++++----- posix/fs/src/open.rs | 9 +- posix/fs/src/path.rs | 11 +- posix/fs/src/pipe.rs | 15 +- posix/fs/src/stat.rs | 5 +- posix/mm/src/memfd.rs | 11 +- posix/mm/src/mmap.rs | 5 +- process/kfd/src/fd_table.rs | 217 +++++++++++++++++++ process/kfd/src/file_descriptor.rs | 38 ++++ process/kfd/src/file_like.rs | 101 +++++++++ process/kfd/src/lib.rs | 329 ++--------------------------- process/kfd/src/stat.rs | 123 +++++++++++ process/kresources/Cargo.toml | 1 + process/kresources/src/lib.rs | 92 +++++++- process/kthread/src/lib.rs | 6 + 39 files changed, 807 insertions(+), 668 deletions(-) create mode 100644 process/kfd/src/fd_table.rs create mode 100644 process/kfd/src/file_descriptor.rs create mode 100644 process/kfd/src/file_like.rs create mode 100644 process/kfd/src/stat.rs diff --git a/core/kservices/src/file/epoll.rs b/core/kservices/src/file/epoll.rs index 01fa8077..fe1ccb4d 100644 --- a/core/kservices/src/file/epoll.rs +++ b/core/kservices/src/file/epoll.rs @@ -18,13 +18,11 @@ use core::{ use bitflags::bitflags; use hashbrown::HashMap; use kerrno::{KError, KResult}; -use kfd::FdTable; use kpoll::{IoEvents, PollSet, Pollable}; use kspin::SpinNoPreempt; -use ksync::RwLock; use linux_raw_sys::general::{EPOLLET, EPOLLONESHOT, epoll_event}; -use crate::file::{FileLike, get_file_like}; +use crate::file::FileLike; pub struct EpollEvent { /// Interested I/O events. @@ -107,8 +105,8 @@ struct EntryKey { file: Weak, } impl EntryKey { - fn new(fd_table: &RwLock, fd: i32) -> KResult { - let file = get_file_like(fd_table, fd)?; + fn new_for_current(fd: i32) -> KResult { + let file = kthread::current_resources().get_file_like(fd)?; Ok(Self { fd, file: Arc::downgrade(&file), @@ -319,8 +317,7 @@ impl Epoll { /// Adds a file descriptor interest to the epoll instance. pub fn add(&self, fd: i32, event: EpollEvent, flags: EpollFlags) -> KResult<()> { - let fd_table = kthread::current_process_state().resources.fd_table(); - let key = EntryKey::new(&fd_table, fd)?; + let key = EntryKey::new_for_current(fd)?; let interest = Arc::new(EpollInterest::new(key.clone(), event, flags)); let mut guard = self.inner.interests.lock(); if guard.contains_key(&key) { @@ -335,8 +332,7 @@ impl Epoll { /// Modifies an existing interest for the given file descriptor. pub fn modify(&self, fd: i32, event: EpollEvent, flags: EpollFlags) -> KResult<()> { - let fd_table = kthread::current_process_state().resources.fd_table(); - let key = EntryKey::new(&fd_table, fd)?; + let key = EntryKey::new_for_current(fd)?; let interest = Arc::new(EpollInterest::new(key.clone(), event, flags)); let mut guard = self.inner.interests.lock(); @@ -359,8 +355,7 @@ impl Epoll { /// Removes an existing interest for the given file descriptor. pub fn delete(&self, fd: i32) -> KResult<()> { - let fd_table = kthread::current_process_state().resources.fd_table(); - let key = EntryKey::new(&fd_table, fd)?; + let key = EntryKey::new_for_current(fd)?; self.inner .interests .lock() diff --git a/core/kservices/src/file/fs.rs b/core/kservices/src/file/fs.rs index 60cae266..e217891b 100644 --- a/core/kservices/src/file/fs.rs +++ b/core/kservices/src/file/fs.rs @@ -19,10 +19,9 @@ use kfs::{FS_CONTEXT, FsContext}; use kpoll::{IoEvents, Pollable}; use ksync::{Mutex, RwLock}; use ktask::future::{block_on, poll_io}; -use kthread::current_process_state; use linux_raw_sys::general::{AT_EMPTY_PATH, AT_FDCWD, AT_SYMLINK_NOFOLLOW}; -use super::{FileLike, Kstat, get_file_like}; +use super::{FileLike, Kstat}; use crate::file::{IoDst, IoSrc}; /// Executes a function with the file system context for the given directory file descriptor. @@ -34,8 +33,8 @@ pub fn with_fs(dirfd: c_int, f: impl FnOnce(&mut FsContext) -> KResult) -> if dirfd == AT_FDCWD { f(&mut fs) } else { - let fd_table = current_process_state().resources.fd_table(); - let dir = Directory::from_fd(&fd_table, dirfd)?.inner.clone(); + let dir = kthread::current_resources().get_file_like_as::(dirfd)?; + let dir = dir.inner.clone(); f(&mut fs.with_current_dir(dir)?) } } @@ -78,8 +77,7 @@ pub fn resolve_at(dirfd: c_int, path: Option<&str>, flags: u32) -> KResult() { ResolveAtResult::File(file.inner().backend()?.location().clone()) @@ -220,13 +218,17 @@ impl FileLike for File { where Self: Sized + 'static, { - get_file_like(fd_table, fd)?.downcast_arc().map_err(|any| { - if any.is::() { - KError::IsADirectory - } else { - KError::BrokenPipe - } - }) + fd_table + .read() + .get_file_like(fd)? + .downcast_arc() + .map_err(|any| { + if any.is::() { + KError::IsADirectory + } else { + KError::BrokenPipe + } + }) } } impl Pollable for File { @@ -288,7 +290,9 @@ impl FileLike for Directory { /// Converts a file descriptor to a directory reference. fn from_fd(fd_table: &RwLock, fd: c_int) -> KResult> { - get_file_like(fd_table, fd)? + fd_table + .read() + .get_file_like(fd)? .downcast_arc() .map_err(|_| KError::NotADirectory) } diff --git a/core/kservices/src/file/mod.rs b/core/kservices/src/file/mod.rs index fd067988..2072d4a2 100644 --- a/core/kservices/src/file/mod.rs +++ b/core/kservices/src/file/mod.rs @@ -16,11 +16,8 @@ pub mod timerfd; use alloc::sync::Arc; use kerrno::{KError, KResult}; -use kfd::{FdTable, FileDescriptor}; -pub use kfd::{ - FileLike, IoDst, IoSrc, Kstat, ReadBuf, WriteBuf, add_file_like, close_all_fds, - close_file_like, get_file_like, new_fd_table, -}; +use kfd::FdTable; +pub use kfd::{FileLike, IoDst, IoSrc, Kstat, ReadBuf, WriteBuf}; use kfs::{FS_CONTEXT, OpenOptions}; use linux_raw_sys::general::{O_RDONLY, O_WRONLY}; @@ -44,22 +41,13 @@ pub fn add_stdio(fd_table: &mut FdTable) -> KResult<()> { let tty_in = open(OpenOptions::new().read(true).write(false), O_RDONLY as _)?; let tty_out = open(OpenOptions::new().read(false).write(true), O_WRONLY as _)?; fd_table - .add(FileDescriptor { - inner: tty_in, - cloexec: false, - }) + .insert_file_like(tty_in, false) .map_err(|_| KError::TooManyOpenFiles)?; fd_table - .add(FileDescriptor { - inner: tty_out.clone(), - cloexec: false, - }) + .insert_file_like(tty_out.clone(), false) .map_err(|_| KError::TooManyOpenFiles)?; fd_table - .add(FileDescriptor { - inner: tty_out, - cloexec: false, - }) + .insert_file_like(tty_out, false) .map_err(|_| KError::TooManyOpenFiles)?; Ok(()) diff --git a/core/kservices/src/file/net.rs b/core/kservices/src/file/net.rs index 6ed4a60f..228e16b9 100644 --- a/core/kservices/src/file/net.rs +++ b/core/kservices/src/file/net.rs @@ -18,7 +18,7 @@ use ksync::RwLock; use linux_raw_sys::general::{O_RDWR, S_IFSOCK}; use super::{FileLike, Kstat}; -use crate::file::{IoDst, IoSrc, get_file_like}; +use crate::file::{IoDst, IoSrc}; /// Socket wrapper providing file-like interface for network sockets. /// @@ -85,7 +85,9 @@ impl FileLike for Socket { where Self: Sized + 'static, { - get_file_like(fd_table, fd)? + fd_table + .read() + .get_file_like(fd)? .downcast_arc() .map_err(|_| KError::NotASocket) } diff --git a/core/kservices/src/task.rs b/core/kservices/src/task.rs index eac7576b..83b6af56 100644 --- a/core/kservices/src/task.rs +++ b/core/kservices/src/task.rs @@ -204,7 +204,7 @@ pub fn do_exit(exit_code: i32, group_exit: bool) { // Close all file descriptors before marking the process as exited. // This ensures pipe write ends and other resources are properly released, // so parent processes blocking on pipe reads will receive EOF. - crate::file::close_all_fds(&thr.proc_state.resources.fd_table()); + thr.proc_state.resources.close_all_fds(); process.exit(); if let Some(parent) = process.parent() { diff --git a/core/kservices/src/vfs/dev/loop.rs b/core/kservices/src/vfs/dev/loop.rs index 12a62ee9..0a977452 100644 --- a/core/kservices/src/vfs/dev/loop.rs +++ b/core/kservices/src/vfs/dev/loop.rs @@ -12,15 +12,12 @@ use kcore::vfs::{DeviceMmap, DeviceOps}; use kerrno::{KError, KResult, LinuxError}; use kfs::FileBackend; use ksync::Mutex; -use kthread::current_process_state; use linux_raw_sys::{ ioctl::{BLKGETSIZE, BLKGETSIZE64, BLKRAGET, BLKRASET, BLKROGET, BLKROSET}, loop_device::{LOOP_CLR_FD, LOOP_GET_STATUS, LOOP_SET_FD, LOOP_SET_STATUS, loop_info}, }; use osvm::{VirtMutPtr, VirtPtr}; -use crate::file::get_file_like; - /// /dev/loopX devices /// Loop device for attaching regular files as block devices pub struct LoopDevice { @@ -92,8 +89,7 @@ impl DeviceOps for LoopDevice { if fd < 0 { return Err(KError::BadFileDescriptor); } - let fd_table = current_process_state().resources.fd_table(); - let f = get_file_like(&fd_table, fd)?; + let f = kthread::current_resources().get_file_like(fd)?; let Some(file) = f.downcast_ref::() else { return Err(KError::InvalidInput); }; diff --git a/core/kservices/src/vfs/mod.rs b/core/kservices/src/vfs/mod.rs index ea9bdc28..b7f90ac9 100644 --- a/core/kservices/src/vfs/mod.rs +++ b/core/kservices/src/vfs/mod.rs @@ -42,7 +42,7 @@ fn procfs_fd_path(task: &KtaskRef, fd: u32) -> VfsResult { .read() .get(fd as _) .ok_or(VfsError::NotFound) - .map(|entry| entry.inner.path().into_owned()) + .map(|entry| entry.inner().path().into_owned()) } /// Mount a filesystem at the specified path, creating the path if it doesn't exist diff --git a/core/ksyscall/Cargo.toml b/core/ksyscall/Cargo.toml index fe36cee1..70a9c8da 100644 --- a/core/ksyscall/Cargo.toml +++ b/core/ksyscall/Cargo.toml @@ -20,7 +20,6 @@ tee = [ [dependencies] kaddr_layout.workspace = true kfd.workspace = true -ksync.workspace = true kservices = { workspace = true } kbuild_config = { workspace = true } kerrno.workspace = true diff --git a/core/ksyscall/src/fs/event.rs b/core/ksyscall/src/fs/event.rs index dd2b1fd3..cc3c9c02 100644 --- a/core/ksyscall/src/fs/event.rs +++ b/core/ksyscall/src/fs/event.rs @@ -10,7 +10,7 @@ use bitflags::bitflags; use kerrno::{KError, KResult}; -use kfd::{FileLike, add_file_like}; +use kfd::FileLike; use kservices::file::event::EventFd; use linux_raw_sys::general::{EFD_CLOEXEC, EFD_NONBLOCK, EFD_SEMAPHORE}; @@ -33,17 +33,9 @@ pub fn sys_eventfd2(initval: u32, flags: u32) -> KResult { let flags = EventFdFlags::from_bits(flags).ok_or(KError::InvalidInput)?; - let proc_state = kthread::current_process_state(); - let fd_table = proc_state.resources.fd_table(); - let max_nofile = proc_state.resources.max_nofile(); - let event_fd = EventFd::new(initval as _, flags.contains(EventFdFlags::SEMAPHORE)); event_fd.set_nonblocking(flags.contains(EventFdFlags::NONBLOCK))?; - add_file_like( - &fd_table, - max_nofile, - event_fd as _, - flags.contains(EventFdFlags::CLOEXEC), - ) - .map(|fd| fd as _) + kthread::current_resources() + .add_file_like(event_fd as _, flags.contains(EventFdFlags::CLOEXEC)) + .map(|fd| fd as _) } diff --git a/core/ksyscall/src/fs/pidfd.rs b/core/ksyscall/src/fs/pidfd.rs index 879e135f..004c6552 100644 --- a/core/ksyscall/src/fs/pidfd.rs +++ b/core/ksyscall/src/fs/pidfd.rs @@ -9,8 +9,9 @@ //! - Pidfd operations (pidfd_getfd, pidfd_send_signal, etc.) //! - Process monitoring through pidfds +use alloc::sync::Arc; + use kerrno::{KError, KResult}; -use kfd::{FileLike, add_file_like}; use kservices::file::PidFd; use ksignal::SignalInfo; use kthread::{get_process_state, send_signal_to_process}; @@ -35,10 +36,8 @@ pub fn sys_pidfd_open(pid: u32, flags: u32) -> KResult { let fd = PidFd::new(&task); // Add the pidfd to the current process's file descriptor table - let proc_state = kthread::current_process_state(); - let fd_table = proc_state.resources.fd_table(); - let max_nofile = proc_state.resources.max_nofile(); - fd.add_to_fd_table(&fd_table, max_nofile, true) + kthread::current_resources() + .add_file_like(Arc::new(fd), true) .map(|fd| fd as _) } @@ -50,9 +49,7 @@ pub fn sys_pidfd_open(pid: u32, flags: u32) -> KResult { pub fn sys_pidfd_getfd(pidfd: i32, target_fd: i32, flags: u32) -> KResult { debug!("sys_pidfd_getfd <= pidfd: {pidfd}, target_fd: {target_fd}, flags: {flags}"); - let current_fd_table = kthread::current_process_state().resources.fd_table(); - // Get the pidfd object and validate it - let pidfd = PidFd::from_fd(¤t_fd_table, pidfd)?; + let pidfd = kthread::current_resources().get_file_like_as::(pidfd)?; // Get the process state that this pidfd refers to let proc_state = pidfd.process_state()?; // Access the target process's file descriptor table within its scope @@ -65,10 +62,7 @@ pub fn sys_pidfd_getfd(pidfd: i32, target_fd: i32, flags: u32) -> KResult .ok_or(KError::BadFileDescriptor) // Duplicate the file and add it to current process's fd table .and_then(|fd| { - let current_proc_state = kthread::current_process_state(); - let fd_table = current_proc_state.resources.fd_table(); - let max_nofile = current_proc_state.resources.max_nofile(); - let fd = add_file_like(&fd_table, max_nofile, fd.inner.clone(), true)?; + let fd = kthread::current_resources().add_file_like(fd.inner().clone(), true)?; Ok(fd as isize) }) } @@ -89,9 +83,7 @@ pub fn sys_pidfd_send_signal( return Err(KError::InvalidInput); } - let fd_table = kthread::current_process_state().resources.fd_table(); - // Get the pidfd object and retrieve the process it refers to - let pidfd = PidFd::from_fd(&fd_table, pidfd)?; + let pidfd = kthread::current_resources().get_file_like_as::(pidfd)?; let pid = pidfd.process_state()?.proc.pid(); // Create signal info from user-provided data and send the signal diff --git a/core/ksyscall/src/fs/signalfd.rs b/core/ksyscall/src/fs/signalfd.rs index c5f1b8a2..9d8205c4 100644 --- a/core/ksyscall/src/fs/signalfd.rs +++ b/core/ksyscall/src/fs/signalfd.rs @@ -16,7 +16,7 @@ use linux_raw_sys::general::{O_CLOEXEC, O_NONBLOCK}; use osvm::VirtPtr; use posix_signal::check_sigset_size; -use crate::file::{FileLike, add_file_like, signalfd::Signalfd}; +use crate::file::{FileLike, signalfd::Signalfd}; // SFD flag definitions (if not available in linux_raw_sys) const SFD_CLOEXEC: u32 = O_CLOEXEC; @@ -63,12 +63,9 @@ pub fn sys_signalfd4( // Read the signal mask from user space before handling the request mode. let mask = unsafe { mask.read_uninit()?.assume_init() }; - let proc_state = kthread::current_process_state(); - let fd_table = proc_state.resources.fd_table(); - // If fd is not -1, we should modify the existing signalfd if fd != -1 { - let signalfd = Signalfd::from_fd(&fd_table, fd)?; + let signalfd = kthread::current_resources().get_file_like_as::(fd)?; signalfd.update_mask(mask); signalfd.set_nonblocking(flags.contains(SignalfdFlags::NONBLOCK))?; return Ok(fd as _); @@ -78,13 +75,7 @@ pub fn sys_signalfd4( let signalfd = Signalfd::new(mask); signalfd.set_nonblocking(flags.contains(SignalfdFlags::NONBLOCK))?; - // Add to file descriptor table - let max_nofile = proc_state.resources.max_nofile(); - add_file_like( - &fd_table, - max_nofile, - signalfd as _, - flags.contains(SignalfdFlags::CLOEXEC), - ) - .map(|fd| fd as _) + kthread::current_resources() + .add_file_like(signalfd as _, flags.contains(SignalfdFlags::CLOEXEC)) + .map(|fd| fd as _) } diff --git a/core/ksyscall/src/fs/timerfd.rs b/core/ksyscall/src/fs/timerfd.rs index 5cb45dc7..79b03c69 100644 --- a/core/ksyscall/src/fs/timerfd.rs +++ b/core/ksyscall/src/fs/timerfd.rs @@ -10,7 +10,7 @@ //! - Timer query (timerfd_gettime) use kerrno::{KError, KResult}; -use kfd::{FileLike, add_file_like}; +use kfd::FileLike; use kservices::file::timerfd::TimerFd; use linux_raw_sys::general::{ CLOCK_BOOTTIME, CLOCK_MONOTONIC, CLOCK_REALTIME, TFD_CLOEXEC, TFD_NONBLOCK, TFD_TIMER_ABSTIME, @@ -35,16 +35,14 @@ pub fn sys_timerfd_create(clock_id: i32, flags: u32) -> KResult { return Err(KError::InvalidInput); } - let proc_state = kthread::current_process_state(); - let fd_table = proc_state.resources.fd_table(); - let max_nofile = proc_state.resources.max_nofile(); - let tfd = TimerFd::new(clock_id as u32); if flags & TFD_NONBLOCK != 0 { tfd.set_nonblocking(true)?; } - add_file_like(&fd_table, max_nofile, tfd as _, flags & TFD_CLOEXEC != 0).map(|fd| fd as _) + kthread::current_resources() + .add_file_like(tfd as _, flags & TFD_CLOEXEC != 0) + .map(|fd| fd as _) } /// Arms or disarms the timer referred to by `fd`. @@ -69,8 +67,7 @@ pub fn sys_timerfd_settime( let value = new.it_value.try_into_time_value()?; let interval = new.it_interval.try_into_time_value()?; - let fd_table = kthread::current_process_state().resources.fd_table(); - let tfd = TimerFd::from_fd(&fd_table, fd)?; + let tfd = kthread::current_resources().get_file_like_as::(fd)?; let (old_interval, old_remaining) = tfd.settime(absolute, value, interval); if let Some(old_value) = old_value.check_non_null() { @@ -87,8 +84,7 @@ pub fn sys_timerfd_settime( pub fn sys_timerfd_gettime(fd: i32, curr_value: *mut itimerspec) -> KResult { debug!("sys_timerfd_gettime <= fd: {fd}"); - let fd_table = kthread::current_process_state().resources.fd_table(); - let tfd = TimerFd::from_fd(&fd_table, fd)?; + let tfd = kthread::current_resources().get_file_like_as::(fd)?; let (interval, remaining) = tfd.gettime(); curr_value.write_vm(itimerspec { diff --git a/core/ksyscall/src/io_mpx/epoll.rs b/core/ksyscall/src/io_mpx/epoll.rs index 116bbe1c..da5ee1fe 100644 --- a/core/ksyscall/src/io_mpx/epoll.rs +++ b/core/ksyscall/src/io_mpx/epoll.rs @@ -10,6 +10,7 @@ //! - Event waiting (epoll_wait, epoll_pwait, etc.) //! - High-performance event notification +use alloc::sync::Arc; use core::time::Duration; use bitflags::bitflags; @@ -28,10 +29,7 @@ use linux_raw_sys::general::{ use posix_signal::check_sigset_size; use posix_types::TimeValueLike; -use crate::file::{ - FileLike, - epoll::{Epoll, EpollEvent, EpollFlags}, -}; +use crate::file::epoll::{Epoll, EpollEvent, EpollFlags}; bitflags! { /// Flags for the `epoll_create` syscall. @@ -45,13 +43,10 @@ bitflags! { pub fn sys_epoll_create1(flags: u32) -> KResult { let flags = EpollCreateFlags::from_bits(flags).ok_or(KError::InvalidInput)?; debug!("sys_epoll_create1 <= flags: {flags:?}"); - let proc_state = kthread::current_process_state(); - let fd_table = proc_state.resources.fd_table(); - let max_nofile = proc_state.resources.max_nofile(); - Epoll::new() - .add_to_fd_table( - &fd_table, - max_nofile, + let resources = kthread::current_resources(); + resources + .add_file_like( + Arc::new(Epoll::new()), flags.contains(EpollCreateFlags::CLOEXEC), ) .map(|fd| fd as isize) @@ -64,8 +59,7 @@ pub fn sys_epoll_ctl( fd: i32, event: UserConstPtr, ) -> KResult { - let fd_table = kthread::current_process_state().resources.fd_table(); - let epoll = Epoll::from_fd(&fd_table, epfd)?; + let epoll = kthread::current_resources().get_file_like_as::(epfd)?; debug!("sys_epoll_ctl <= epfd: {epfd}, op: {op}, fd: {fd}"); let parse_event = || -> KResult<(EpollEvent, EpollFlags)> { @@ -110,8 +104,7 @@ fn do_epoll_wait( check_sigset_size(sigsetsize)?; debug!("sys_epoll_wait <= epfd: {epfd}, maxevents: {maxevents}, timeout: {timeout:?}"); - let fd_table = kthread::current_process_state().resources.fd_table(); - let epoll = Epoll::from_fd(&fd_table, epfd)?; + let epoll = kthread::current_resources().get_file_like_as::(epfd)?; if maxevents <= 0 { return Err(KError::InvalidInput); diff --git a/core/ksyscall/src/io_mpx/poll.rs b/core/ksyscall/src/io_mpx/poll.rs index 0b63863c..92400e1d 100644 --- a/core/ksyscall/src/io_mpx/poll.rs +++ b/core/ksyscall/src/io_mpx/poll.rs @@ -35,7 +35,6 @@ fn do_poll( ) -> KResult { debug!("do_poll fds={poll_fds:?} timeout={timeout:?}"); - let fd_table = kthread::current_process_state().resources.fd_table(); let mut res = 0isize; let mut fds = Vec::with_capacity(poll_fds.len()); let mut revents = Vec::with_capacity(poll_fds.len()); @@ -44,7 +43,7 @@ fn do_poll( // Skip -1 continue; } - match kfd::get_file_like(&fd_table, fd.fd) { + match kthread::current_resources().get_file_like(fd.fd) { Ok(f) => { fds.push(( f, diff --git a/core/ksyscall/src/io_mpx/select.rs b/core/ksyscall/src/io_mpx/select.rs index 7e30d926..b5ea62d0 100644 --- a/core/ksyscall/src/io_mpx/select.rs +++ b/core/ksyscall/src/io_mpx/select.rs @@ -160,7 +160,7 @@ fn do_select( let f = fd_table .get(fd) .ok_or(KError::BadFileDescriptor)? - .inner + .inner() .clone(); let mut events = IoEvents::empty(); events.set(IoEvents::IN, is_read); diff --git a/core/ksyscall/src/net/cmsg.rs b/core/ksyscall/src/net/cmsg.rs index e6628b65..793ebbb2 100644 --- a/core/ksyscall/src/net/cmsg.rs +++ b/core/ksyscall/src/net/cmsg.rs @@ -120,7 +120,7 @@ pub enum CMsg { } impl CMsg { /// Parse a control message header and extract its data - pub fn parse(fd_table: &ksync::RwLock, hdr: &cmsghdr) -> KResult { + pub fn parse(resources: &kthread::ProcessResources, hdr: &cmsghdr) -> KResult { if hdr.cmsg_len < size_of::() { return Err(KError::InvalidInput); } @@ -139,7 +139,7 @@ impl CMsg { if fd < 0 { return Err(KError::BadFileDescriptor); } - let f = kfd::get_file_like(fd_table, fd)?; + let f = resources.get_file_like(fd)?; fds.push(f); } Self::Rights { fds } diff --git a/core/ksyscall/src/net/io.rs b/core/ksyscall/src/net/io.rs index cb976c84..7fc69c23 100644 --- a/core/ksyscall/src/net/io.rs +++ b/core/ksyscall/src/net/io.rs @@ -14,7 +14,6 @@ use alloc::{boxed::Box, sync::Arc, vec::Vec}; use core::{any::TypeId, net::Ipv4Addr, time::Duration}; use kerrno::{KError, KResult, LinuxError}; -use kfd::FdTable; use khal::time::wall_time; use kio::prelude::*; use knet::{ @@ -44,7 +43,7 @@ fn parse_recvmmsg_timeout(timeout: UserConstPtr) -> KResult>, + resources: &kthread::ProcessResources, control_ptr: usize, control_len: usize, ) -> KResult> { @@ -66,7 +65,7 @@ fn parse_send_cmsgs( return Err(KError::InvalidInput); } - cmsg.push(Box::new(CMsg::parse(fd_table, hdr)?) as CMsgData); + cmsg.push(Box::new(CMsg::parse(resources, hdr)?) as CMsgData); ptr += hdr.cmsg_len; } @@ -74,7 +73,7 @@ fn parse_send_cmsgs( } use crate::{ - file::{FileLike, Socket, add_file_like}, + file::{FileLike, Socket}, io::{IoVec, IoVectorBuf}, net::{CMsg, CMsgBuilder}, socket::SocketAddrExt, @@ -107,8 +106,7 @@ fn into_socket_cmsg(cmsg: CMsgData) -> Option { } fn push_socket_cmsg( - fd_table: &Arc>, - max_nofile: u64, + resources: &kthread::ProcessResources, builder: &mut CMsgBuilder<'_>, cmsg: SocketCmsg, ) -> KResult { @@ -127,7 +125,7 @@ fn push_socket_cmsg( .into_iter() .zip(data[..body_len].chunks_exact_mut(size_of::())) { - let fd = add_file_like(fd_table, max_nofile, f, false)?; + let fd = resources.add_file_like(f, false)?; chunk.copy_from_slice(&fd.to_ne_bytes()); written += size_of::(); } @@ -154,8 +152,7 @@ fn send_impl( debug!("sys_send <= fd: {fd}, flags: {flags}, addr: {addr:?}"); - let fd_table = kthread::current_process_state().resources.fd_table(); - let socket = Socket::from_fd(&fd_table, fd)?; + let socket = kthread::current_resources().get_file_like_as::(fd)?; let sent = socket.send( &mut src, SendOptions { @@ -183,8 +180,12 @@ pub fn sys_sendto( /// Send data with vectored I/O and ancillary data (control messages) pub fn sys_sendmsg(fd: i32, msg: UserConstPtr, flags: u32) -> KResult { let msg = msg.get_as_ref()?; - let fd_table = kthread::current_process_state().resources.fd_table(); - let cmsg = parse_send_cmsgs(&fd_table, msg.msg_control as usize, msg.msg_controllen)?; + let resources = kthread::current_resources(); + let cmsg = parse_send_cmsgs( + resources.as_ref(), + msg.msg_control as usize, + msg.msg_controllen, + )?; send_impl( fd, IoVectorBuf::new(msg.msg_iov as *const IoVec, msg.msg_iovlen)?.into_io(), @@ -208,9 +209,8 @@ fn recv_impl( debug!("sys_recv <= fd: {fd}, flags: {flags}"); let proc_state = kthread::current_process_state(); - let fd_table = proc_state.resources.fd_table(); - let max_nofile = proc_state.resources.max_nofile(); - let socket = Socket::from_fd(&fd_table, fd)?; + let resources = proc_state.resources.clone(); + let socket = resources.get_file_like_as::(fd)?; let mut recv_flags = RecvFlags::empty(); if flags & MSG_PEEK != 0 { recv_flags |= RecvFlags::PEEK; @@ -246,7 +246,7 @@ fn recv_impl( warn!("received unexpected cmsg"); continue; }; - let push_result = push_socket_cmsg(&fd_table, max_nofile, &mut builder, cmsg); + let push_result = push_socket_cmsg(resources.as_ref(), &mut builder, cmsg); match push_result { Ok(true) => {} @@ -326,11 +326,11 @@ pub fn sys_sendmmsg(fd: i32, msgvec: UserPtr, vlen: u32, flags: u32) -> } let msgvec = msgvec.get_as_mut_slice(vlen as usize)?; - let fd_table = kthread::current_process_state().resources.fd_table(); + let resources = kthread::current_resources(); let mut sent = 0; for msg in msgvec.iter_mut() { let cmsg = parse_send_cmsgs( - &fd_table, + resources.as_ref(), msg.msg_hdr.msg_control as usize, msg.msg_hdr.msg_controllen, )?; diff --git a/core/ksyscall/src/net/name.rs b/core/ksyscall/src/net/name.rs index 33b42a20..00196975 100644 --- a/core/ksyscall/src/net/name.rs +++ b/core/ksyscall/src/net/name.rs @@ -14,10 +14,7 @@ use knet::SocketOps; use kservices::mm::UserPtr; use linux_raw_sys::net::{sockaddr, socklen_t}; -use crate::{ - file::{FileLike, Socket}, - socket::SocketAddrExt, -}; +use crate::{file::Socket, socket::SocketAddrExt}; /// Get the local address bound to a socket pub fn sys_getsockname( @@ -25,8 +22,7 @@ pub fn sys_getsockname( addr: UserPtr, addrlen: UserPtr, ) -> KResult { - let fd_table = kthread::current_process_state().resources.fd_table(); - let socket = Socket::from_fd(&fd_table, fd)?; + let socket = kthread::current_resources().get_file_like_as::(fd)?; let local_addr = socket.local_addr()?; debug!("sys_getsockname <= fd: {fd}, addr: {local_addr:?}"); @@ -40,8 +36,7 @@ pub fn sys_getpeername( addr: UserPtr, addrlen: UserPtr, ) -> KResult { - let fd_table = kthread::current_process_state().resources.fd_table(); - let socket = Socket::from_fd(&fd_table, fd)?; + let socket = kthread::current_resources().get_file_like_as::(fd)?; let peer_addr = socket.peer_addr()?; debug!("sys_getpeername <= fd: {fd}, addr: {peer_addr:?}"); diff --git a/core/ksyscall/src/net/opt.rs b/core/ksyscall/src/net/opt.rs index d0bc19fc..b0358834 100644 --- a/core/ksyscall/src/net/opt.rs +++ b/core/ksyscall/src/net/opt.rs @@ -10,7 +10,6 @@ //! - Socket-level, IP-level, TCP-level, and other protocol options use kerrno::{KError, KResult, LinuxError}; -use kfd::FileLike; use knet::options::{Configurable, GetSocketOption, SetSocketOption}; use kservices::{ file::Socket, @@ -149,8 +148,7 @@ pub fn sys_getsockopt( val.cast().get_as_mut() } - let fd_table = kthread::current_process_state().resources.fd_table(); - let socket = Socket::from_fd(&fd_table, fd)?; + let socket = kthread::current_resources().get_file_like_as::(fd)?; macro_rules! dispatch { ($which:ident) => { socket.get_option(GetSocketOption::$which(get(optval, optlen)?))?; @@ -190,8 +188,7 @@ pub fn sys_setsockopt( val.cast().get_as_ref() } - let fd_table = kthread::current_process_state().resources.fd_table(); - let socket = Socket::from_fd(&fd_table, fd)?; + let socket = kthread::current_resources().get_file_like_as::(fd)?; macro_rules! dispatch { ($which:ident) => { socket.set_option(SetSocketOption::$which(get(optval, optlen)?))?; diff --git a/core/ksyscall/src/net/socket.rs b/core/ksyscall/src/net/socket.rs index a02ee78b..683fc16f 100644 --- a/core/ksyscall/src/net/socket.rs +++ b/core/ksyscall/src/net/socket.rs @@ -10,7 +10,7 @@ //! - Socket binding and connection (bind, connect, listen, etc.) //! - Socket shutdown (shutdown, etc.) -use alloc::boxed::Box; +use alloc::{boxed::Box, sync::Arc}; use kerrno::{KError, KResult, LinuxError}; #[cfg(feature = "vsock")] @@ -105,11 +105,8 @@ pub fn sys_socket(domain: u32, raw_ty: u32, proto: u32) -> KResult { } let cloexec = raw_ty & O_CLOEXEC != 0; - let proc_state = kthread::current_process_state(); - let fd_table = proc_state.resources.fd_table(); - let max_nofile = proc_state.resources.max_nofile(); - socket - .add_to_fd_table(&fd_table, max_nofile, cloexec) + kthread::current_resources() + .add_file_like(Arc::new(socket), cloexec) .map(|fd| fd as isize) } @@ -117,8 +114,9 @@ pub fn sys_socket(domain: u32, raw_ty: u32, proto: u32) -> KResult { pub fn sys_bind(fd: i32, addr: UserConstPtr, addrlen: u32) -> KResult { let addr = SocketAddrEx::read_from_user(addr, addrlen)?; - let fd_table = kthread::current_process_state().resources.fd_table(); - Socket::from_fd(&fd_table, fd)?.bind(addr)?; + kthread::current_resources() + .get_file_like_as::(fd)? + .bind(addr)?; Ok(0) } @@ -127,14 +125,16 @@ pub fn sys_bind(fd: i32, addr: UserConstPtr, addrlen: u32) -> KResult< pub fn sys_connect(fd: i32, addr: UserConstPtr, addrlen: u32) -> KResult { let addr = SocketAddrEx::read_from_user(addr, addrlen)?; - let fd_table = kthread::current_process_state().resources.fd_table(); - Socket::from_fd(&fd_table, fd)?.connect(addr).map_err(|e| { - if e == KError::WouldBlock { - KError::InProgress - } else { - e - } - })?; + kthread::current_resources() + .get_file_like_as::(fd)? + .connect(addr) + .map_err(|e| { + if e == KError::WouldBlock { + KError::InProgress + } else { + e + } + })?; Ok(0) } @@ -145,8 +145,9 @@ pub fn sys_listen(fd: i32, backlog: i32) -> KResult { return Err(KError::InvalidInput); } - let fd_table = kthread::current_process_state().resources.fd_table(); - Socket::from_fd(&fd_table, fd)?.listen()?; + kthread::current_resources() + .get_file_like_as::(fd)? + .listen()?; Ok(0) } @@ -165,18 +166,15 @@ pub fn sys_accept4( ) -> KResult { let cloexec = flags & O_CLOEXEC != 0; - let proc_state = kthread::current_process_state(); - let fd_table = proc_state.resources.fd_table(); - let max_nofile = proc_state.resources.max_nofile(); - let socket = Socket::from_fd(&fd_table, fd)?; + let socket = kthread::current_resources().get_file_like_as::(fd)?; let socket = Socket(socket.accept()?); if flags & O_NONBLOCK != 0 { socket.set_nonblocking(true)?; } let remote_addr = socket.peer_addr()?; - let fd = socket - .add_to_fd_table(&fd_table, max_nofile, cloexec) + let fd = kthread::current_resources() + .add_file_like(Arc::new(socket), cloexec) .map(|fd| fd as isize)?; if !addr.is_null() { @@ -188,8 +186,7 @@ pub fn sys_accept4( /// Shut down all or part of a full-duplex connection pub fn sys_shutdown(fd: i32, how: u32) -> KResult { - let fd_table = kthread::current_process_state().resources.fd_table(); - let socket = Socket::from_fd(&fd_table, fd)?; + let socket = kthread::current_resources().get_file_like_as::(fd)?; let how = match how { SHUT_RD => Shutdown::Read, SHUT_WR => Shutdown::Write, @@ -236,12 +233,9 @@ pub fn sys_socketpair( } let cloexec = raw_ty & O_CLOEXEC != 0; - let proc_state = kthread::current_process_state(); - let fd_table = proc_state.resources.fd_table(); - let max_nofile = proc_state.resources.max_nofile(); *fds.get_as_mut()? = [ - sock1.add_to_fd_table(&fd_table, max_nofile, cloexec)?, - sock2.add_to_fd_table(&fd_table, max_nofile, cloexec)?, + kthread::current_resources().add_file_like(Arc::new(sock1), cloexec)?, + kthread::current_resources().add_file_like(Arc::new(sock2), cloexec)?, ]; Ok(0) } diff --git a/core/ksyscall/src/task/clone.rs b/core/ksyscall/src/task/clone.rs index f15edbef..3848d712 100644 --- a/core/ksyscall/src/task/clone.rs +++ b/core/ksyscall/src/task/clone.rs @@ -16,7 +16,7 @@ use alloc::sync::Arc; use bitflags::bitflags; use kcore::mm::copy_from_kernel; use kerrno::{KError, KResult}; -use kfd::{FileLike, new_fd_table}; +use kfd::{FdTable, FileLike}; use kfs::FS_CONTEXT; use khal::uspace::UserContext; use kprocess::Pid; @@ -285,10 +285,7 @@ impl CloneRequest { .resources .replace_fd_table(old_proc_data.resources.fd_table()); } else { - let fd_table = new_fd_table(); - fd_table - .write() - .clone_from(&old_proc_data.resources.fd_table().read()); + let fd_table = FdTable::clone_shared_from(&old_proc_data.resources.fd_table()); proc_state.resources.replace_fd_table(fd_table); } diff --git a/core/ksyscall/src/task/execve.rs b/core/ksyscall/src/task/execve.rs index 6a689d91..23d679d6 100644 --- a/core/ksyscall/src/task/execve.rs +++ b/core/ksyscall/src/task/execve.rs @@ -91,16 +91,7 @@ pub fn sys_execve( kthread::current_thread().set_clear_child_tid(0); // Close CLOEXEC file descriptors - let fd_table = proc_state.resources.fd_table(); - let mut fd_table = fd_table.write(); - let cloexec_fds = fd_table - .ids() - .filter(|it| fd_table.get(*it).unwrap().cloexec) - .collect::>(); - for fd in cloexec_fds { - fd_table.remove(fd); - } - drop(fd_table); + proc_state.resources.close_cloexec_files(); uctx.set_ip(entry_point.as_usize()); uctx.set_sp(user_stack_base.as_usize()); diff --git a/posix/fs/src/ctl.rs b/posix/fs/src/ctl.rs index c21376db..431090c7 100644 --- a/posix/fs/src/ctl.rs +++ b/posix/fs/src/ctl.rs @@ -21,7 +21,6 @@ use core::{ use fs_ng_vfs::{MetadataUpdate, NodePermission, NodeType, path::Path}; use kerrno::{KError, KResult}; -use kfd::get_file_like; use kfs::{FS_CONTEXT, FsContext}; use khal::time::wall_time; use kservices::file::Directory; @@ -37,8 +36,7 @@ use crate::path::{resolve_at, with_fs}; /// of special files. pub fn sys_ioctl(fd: i32, cmd: u32, arg: usize) -> KResult { debug!("sys_ioctl <= fd: {fd}, cmd: {cmd}, arg: {arg}"); - let fd_table = kthread::current_process_state().resources.fd_table(); - let f = get_file_like(&fd_table, fd)?; + let f = kthread::current_resources().get_file_like(fd)?; if cmd == FIONBIO { let val = (arg as *const u8).read_vm()?; if val != 0 && val != 1 { @@ -178,8 +176,7 @@ pub fn sys_getdents64(fd: i32, buf: UserPtr, len: usize) -> KResult { let mut buffer = DirBuffer::new(len); - let fd_table = kthread::current_process_state().resources.fd_table(); - let dir = Directory::from_fd(&fd_table, fd)?; + let dir = kthread::current_resources().get_file_like_as::(fd)?; let mut dir_offset = dir.offset.lock(); let mut has_remaining = false; @@ -574,8 +571,7 @@ pub fn sys_sync() -> KResult { } pub fn sys_syncfs(fd: i32) -> KResult { - let fd_table = kthread::current_process_state().resources.fd_table(); - let file_like = get_file_like(&fd_table, fd)?; + let file_like = kthread::current_resources().get_file_like(fd)?; if let Some(file) = file_like.downcast_ref::() { file.inner().location().filesystem().flush()?; diff --git a/posix/fs/src/fd_ops.rs b/posix/fs/src/fd_ops.rs index 0314cac1..566a25e1 100644 --- a/posix/fs/src/fd_ops.rs +++ b/posix/fs/src/fd_ops.rs @@ -13,9 +13,6 @@ use core::ffi::c_int; use bitflags::bitflags; use kerrno::{KError, KResult}; -use kfd::{ - FileLike, add_file_like, close_file_like, get_file_like, new_fd_table, -}; use kservices::file::Pipe; use linux_raw_sys::general::*; use posix_types::UserPtr; @@ -23,9 +20,7 @@ use posix_types::UserPtr; /// Closes the specified file descriptor. pub fn sys_close(fd: c_int) -> KResult { debug!("sys_close <= {fd}"); - let proc_state = kthread::current_process_state(); - let fd_table = proc_state.resources.fd_table(); - close_file_like(&fd_table, fd)?; + kthread::current_resources().close_file_like(fd)?; Ok(0) } @@ -47,25 +42,13 @@ pub fn sys_close_range(first: i32, last: i32, flags: u32) -> KResult { let proc_state = kthread::current_process_state(); if flags.contains(CloseRangeFlags::UNSHARE) { - let old_files = proc_state.resources.fd_table(); - let new_files = new_fd_table(); - new_files.write().clone_from(&old_files.read()); - proc_state.resources.replace_fd_table(new_files); + proc_state.resources.unshare_fd_table(); } - let cloexec = flags.contains(CloseRangeFlags::CLOEXEC); - let fd_table = proc_state.resources.fd_table(); - let mut fd_table = fd_table.write(); - if let Some(max_index) = fd_table.ids().next_back() { - for fd in first..=last.min(max_index as i32) { - if cloexec { - if let Some(f) = fd_table.get_mut(fd as _) { - f.cloexec = true; - } - } else { - fd_table.remove(fd as _); - } - } + if flags.contains(CloseRangeFlags::CLOEXEC) { + proc_state.resources.set_cloexec_range(first, last); + } else { + proc_state.resources.close_range(first, last); } Ok(0) @@ -74,10 +57,7 @@ pub fn sys_close_range(first: i32, last: i32, flags: u32) -> KResult { /// Duplicates a file descriptor and optionally sets `CLOEXEC`. fn dup_fd(old_fd: c_int, cloexec: bool) -> KResult { let proc_state = kthread::current_process_state(); - let fd_table = proc_state.resources.fd_table(); - let max_nofile = proc_state.resources.max_nofile(); - let f = get_file_like(&fd_table, old_fd)?; - let new_fd = add_file_like(&fd_table, max_nofile, f, cloexec)?; + let new_fd = proc_state.resources.duplicate_file_like(old_fd, cloexec)?; Ok(new_fd as _) } @@ -91,8 +71,7 @@ pub fn sys_dup(old_fd: c_int) -> KResult { /// Duplicates a file descriptor to a specific target fd. pub fn sys_dup2(old_fd: c_int, new_fd: c_int) -> KResult { if old_fd == new_fd { - let fd_table = kthread::current_process_state().resources.fd_table(); - get_file_like(&fd_table, new_fd)?; + kthread::current_resources().get_file_like(new_fd)?; return Ok(new_fd as _); } sys_dup3(old_fd, new_fd, 0) @@ -114,20 +93,10 @@ pub fn sys_dup3(old_fd: c_int, new_fd: c_int, flags: c_int) -> KResult { return Err(KError::InvalidInput); } - let fd_table = kthread::current_process_state().resources.fd_table(); - let mut fd_table = fd_table.write(); - let mut f = fd_table - .get(old_fd as _) - .cloned() - .ok_or(KError::BadFileDescriptor)?; - f.cloexec = flags.contains(Dup3Flags::O_CLOEXEC); - - fd_table.remove(new_fd as _); - fd_table - .add_at(new_fd as _, f) - .map_err(|_| KError::BadFileDescriptor)?; - - Ok(new_fd as _) + kthread::current_process_state() + .resources + .duplicate_file_like_to(old_fd, new_fd, flags.contains(Dup3Flags::O_CLOEXEC)) + .map(|fd| fd as _) } /// Performs file descriptor control operations. @@ -147,13 +116,13 @@ pub fn sys_fcntl(fd: c_int, cmd: c_int, arg: usize) -> KResult { Ok(0) } F_SETFL => { - let fd_table = kthread::current_process_state().resources.fd_table(); - get_file_like(&fd_table, fd)?.set_nonblocking(arg & (O_NONBLOCK as usize) > 0)?; + kthread::current_resources() + .get_file_like(fd)? + .set_nonblocking(arg & (O_NONBLOCK as usize) > 0)?; Ok(0) } F_GETFL => { - let fd_table = kthread::current_process_state().resources.fd_table(); - let f = get_file_like(&fd_table, fd)?; + let f = kthread::current_resources().get_file_like(fd)?; let mut ret = f.open_flags(); if f.nonblocking() { @@ -163,32 +132,22 @@ pub fn sys_fcntl(fd: c_int, cmd: c_int, arg: usize) -> KResult { Ok(ret as _) } F_GETFD => { - let fd_table = kthread::current_process_state().resources.fd_table(); - let cloexec = fd_table - .read() - .get(fd as _) - .ok_or(KError::BadFileDescriptor)? - .cloexec; + let cloexec = kthread::current_process_state().resources.cloexec(fd)?; Ok(if cloexec { FD_CLOEXEC as _ } else { 0 }) } F_SETFD => { let cloexec = arg & FD_CLOEXEC as usize != 0; - let fd_table = kthread::current_process_state().resources.fd_table(); - fd_table - .write() - .get_mut(fd as _) - .ok_or(KError::BadFileDescriptor)? - .cloexec = cloexec; + kthread::current_process_state() + .resources + .set_cloexec(fd, cloexec)?; Ok(0) } F_GETPIPE_SZ => { - let fd_table = kthread::current_process_state().resources.fd_table(); - let pipe = Pipe::from_fd(&fd_table, fd)?; + let pipe = kthread::current_resources().get_file_like_as::(fd)?; Ok(pipe.capacity() as _) } F_SETPIPE_SZ => { - let fd_table = kthread::current_process_state().resources.fd_table(); - let pipe = Pipe::from_fd(&fd_table, fd)?; + let pipe = kthread::current_resources().get_file_like_as::(fd)?; pipe.resize(arg)?; Ok(0) } diff --git a/posix/fs/src/io.rs b/posix/fs/src/io.rs index 4606287a..6178c721 100644 --- a/posix/fs/src/io.rs +++ b/posix/fs/src/io.rs @@ -18,7 +18,7 @@ use core::{ }; use kerrno::{KError, KResult, LinuxError}; -use kfd::{FileLike, get_file_like}; +use kfd::FileLike; use kfs::{FS_CONTEXT, FileFlags, OpenOptions}; use kio::{Seek, SeekFrom}; use kpoll::{IoEvents, Pollable}; @@ -76,11 +76,8 @@ pub fn sys_dummy_fd(sysno: Sysno) -> KResult { return Err(KError::Unsupported); } warn!("Dummy fd created: {sysno}"); - let proc_state = kthread::current_process_state(); - let fd_table = proc_state.resources.fd_table(); - let max_nofile = proc_state.resources.max_nofile(); - DummyFd - .add_to_fd_table(&fd_table, max_nofile, false) + kthread::current_resources() + .add_file_like(Arc::new(DummyFd), false) .map(|fd| fd as isize) } @@ -90,16 +87,16 @@ pub fn sys_dummy_fd(sysno: Sysno) -> KResult { pub fn sys_read(fd: i32, buf: UserPtr, len: usize) -> KResult { debug!("sys_read <= fd: {fd}, buf: {:p}, len: {len}", buf.as_ptr()); // Get the file object and perform the read operation into the user buffer - let fd_table = kthread::current_process_state().resources.fd_table(); - Ok(get_file_like(&fd_table, fd)?.read(&mut VmBytesMut::new(buf, len))? as _) + Ok(kthread::current_resources() + .get_file_like(fd)? + .read(&mut VmBytesMut::new(buf, len))? as _) } /// Vectored read into multiple buffers. pub fn sys_readv(fd: i32, iov: UserConstPtr, iovcnt: usize) -> KResult { debug!("sys_readv <= fd: {fd}, iovcnt: {iovcnt}"); // Vectored read - read data into multiple buffers in a single operation - let fd_table = kthread::current_process_state().resources.fd_table(); - let f = get_file_like(&fd_table, fd)?; + let f = kthread::current_resources().get_file_like(fd)?; f.read(&mut IoVectorBuf::new(iov, iovcnt)?.into_io()) .map(|n| n as _) } @@ -109,16 +106,16 @@ pub fn sys_readv(fd: i32, iov: UserConstPtr, iovcnt: usize) -> KResult, len: usize) -> KResult { debug!("sys_write <= fd: {fd}, buf: {:p}, len: {len}", buf.as_ptr()); - let fd_table = kthread::current_process_state().resources.fd_table(); - Ok(get_file_like(&fd_table, fd)?.write(&mut VmBytes::new(buf, len))? as _) + Ok(kthread::current_resources() + .get_file_like(fd)? + .write(&mut VmBytes::new(buf, len))? as _) } /// Vectored write from multiple buffers. pub fn sys_writev(fd: i32, iov: UserConstPtr, iovcnt: usize) -> KResult { debug!("sys_writev <= fd: {fd}, iovcnt: {iovcnt}"); // Vectored write - write data from multiple buffers in a single operation - let fd_table = kthread::current_process_state().resources.fd_table(); - let f = get_file_like(&fd_table, fd)?; + let f = kthread::current_resources().get_file_like(fd)?; f.write(&mut IoVectorBuf::new(iov, iovcnt)?.into_io()) .map(|n| n as _) } @@ -133,8 +130,7 @@ pub fn sys_lseek(fd: c_int, offset: __kernel_off_t, whence: c_int) -> KResult SeekFrom::End(offset as _), _ => return Err(KError::InvalidInput), }; - let fd_table = kthread::current_process_state().resources.fd_table(); - let any_file = get_file_like(&fd_table, fd)?; + let any_file = kthread::current_resources().get_file_like(fd)?; if let Ok(f) = any_file.clone().downcast_arc::() { let off = f.inner().seek(pos)?; @@ -181,8 +177,7 @@ pub fn sys_truncate(path: UserConstPtr, length: __kernel_off_t) -> KResu pub fn sys_ftruncate(fd: c_int, length: __kernel_off_t) -> KResult { debug!("sys_ftruncate <= {fd} {length}"); // Truncate file descriptor to specified length - let fd_table = kthread::current_process_state().resources.fd_table(); - let f = File::from_fd(&fd_table, fd)?; + let f = kthread::current_resources().get_file_like_as::(fd)?; f.inner().access(FileFlags::WRITE)?.set_len(length as _)?; Ok(0) } @@ -220,8 +215,7 @@ pub fn sys_fallocate( let keep_size = (mode & FALLOC_FL_KEEP_SIZE) != 0; let base_mode = mode & !FALLOC_FL_KEEP_SIZE; - let fd_table = kthread::current_process_state().resources.fd_table(); - let f = File::from_fd(&fd_table, fd)?; + let f = kthread::current_resources().get_file_like_as::(fd)?; let inner = f.inner(); let file = inner.access(FileFlags::WRITE)?; @@ -334,8 +328,7 @@ pub fn sys_fallocate( pub fn sys_fsync(fd: c_int) -> KResult { debug!("sys_fsync <= {fd}"); // Synchronize file to disk - syncs both data and metadata - let fd_table = kthread::current_process_state().resources.fd_table(); - let any_file = get_file_like(&fd_table, fd)?; + let any_file = kthread::current_resources().get_file_like(fd)?; if let Ok(f) = any_file.clone().downcast_arc::() { f.inner().sync(false)?; return Ok(0); @@ -350,8 +343,7 @@ pub fn sys_fsync(fd: c_int) -> KResult { pub fn sys_fdatasync(fd: c_int) -> KResult { debug!("sys_fdatasync <= {fd}"); // Synchronize file data to disk - only syncs data, not metadata - let fd_table = kthread::current_process_state().resources.fd_table(); - let any_file = get_file_like(&fd_table, fd)?; + let any_file = kthread::current_resources().get_file_like(fd)?; if let Ok(f) = any_file.clone().downcast_arc::() { f.inner().sync(true)?; return Ok(0); @@ -372,8 +364,10 @@ pub fn sys_fadvise64( debug!("sys_fadvise64 <= fd: {fd}, offset: {offset}, len: {len}, advice: {advice}"); // Provide hints to kernel about how file will be accessed // Currently not fully implemented - pipes are not supported - let fd_table = kthread::current_process_state().resources.fd_table(); - if Pipe::from_fd(&fd_table, fd).is_ok() { + if kthread::current_resources() + .get_file_like_as::(fd) + .is_ok() + { return Err(KError::BrokenPipe); } if advice > 5 { @@ -390,8 +384,7 @@ pub fn sys_pread64( offset: __kernel_off_t, ) -> KResult { // Read from file at specific offset without changing file position - let fd_table = kthread::current_process_state().resources.fd_table(); - let f = File::from_fd(&fd_table, fd)?; + let f = kthread::current_resources().get_file_like_as::(fd)?; if offset < 0 { return Err(KError::InvalidInput); } @@ -412,8 +405,7 @@ pub fn sys_pwrite64( if len == 0 { return Ok(0); } - let fd_table = kthread::current_process_state().resources.fd_table(); - let f = File::from_fd(&fd_table, fd)?; + let f = kthread::current_resources().get_file_like_as::(fd)?; let write = f.inner().write_at(VmBytes::new(buf, len), offset as _)?; Ok(write as _) } @@ -450,8 +442,7 @@ pub fn sys_preadv2( ) -> KResult { debug!("sys_preadv2 <= fd: {fd}, iovcnt: {iovcnt}, offset: {offset}, flags: {_flags}"); // Vectored read at specific offset with optional flags - let fd_table = kthread::current_process_state().resources.fd_table(); - let f = File::from_fd(&fd_table, fd)?; + let f = kthread::current_resources().get_file_like_as::(fd)?; f.inner() .read_at( IoVectorBuf::new(iov.as_ptr(), iovcnt)?.into_io(), @@ -470,8 +461,7 @@ pub fn sys_pwritev2( ) -> KResult { debug!("sys_pwritev2 <= fd: {fd}, iovcnt: {iovcnt}, offset: {offset}, flags: {_flags}"); // Vectored write at specific offset with optional flags. - let fd_table = kthread::current_process_state().resources.fd_table(); - let f = File::from_fd(&fd_table, fd)?; + let f = kthread::current_resources().get_file_like_as::(fd)?; f.inner() .write_at( IoVectorBuf::new(iov.as_ptr(), iovcnt)?.into_io(), @@ -579,19 +569,19 @@ pub fn sys_sendfile( ); // Source can use fixed offset or current file position - let fd_table = kthread::current_process_state().resources.fd_table(); + let resources = kthread::current_resources(); let src = if !offset.is_null() { // Check offset fits in 32-bit range (legacy syscall limitation) if offset.read_vm()? > u32::MAX as u64 { return Err(KError::InvalidInput); } - SendFile::Offset(File::from_fd(&fd_table, in_fd)?, offset) + SendFile::Offset(resources.get_file_like_as::(in_fd)?, offset) } else { - SendFile::Direct(get_file_like(&fd_table, in_fd)?) + SendFile::Direct(resources.get_file_like(in_fd)?) }; // Destination always uses current file position - let dst = SendFile::Direct(get_file_like(&fd_table, out_fd)?); + let dst = SendFile::Direct(resources.get_file_like(out_fd)?); do_send(src, dst, len).map(|n| n as _) } @@ -621,18 +611,18 @@ pub fn sys_copy_file_range( // TODO: check same file and overlap // Source can use fixed offset or current file position - let fd_table = kthread::current_process_state().resources.fd_table(); + let resources = kthread::current_resources(); let src = if !off_in.is_null() { - SendFile::Offset(File::from_fd(&fd_table, fd_in)?, off_in) + SendFile::Offset(resources.get_file_like_as::(fd_in)?, off_in) } else { - SendFile::Direct(get_file_like(&fd_table, fd_in)?) + SendFile::Direct(resources.get_file_like(fd_in)?) }; // Destination can also use fixed offset or current file position let dst = if !off_out.is_null() { - SendFile::Offset(File::from_fd(&fd_table, fd_out)?, off_out) + SendFile::Offset(resources.get_file_like_as::(fd_out)?, off_out) } else { - SendFile::Direct(get_file_like(&fd_table, fd_out)?) + SendFile::Direct(resources.get_file_like(fd_out)?) }; do_send(src, dst, len).map(|n| n as _) @@ -661,10 +651,12 @@ pub fn sys_splice( // Track if we have a pipe - at least one must be present for splice let mut has_pipe = false; - let fd_table = kthread::current_process_state().resources.fd_table(); + let resources = kthread::current_resources(); // Dummy file descriptors cannot be spliced - if DummyFd::from_fd(&fd_table, fd_in).is_ok() || DummyFd::from_fd(&fd_table, fd_out).is_ok() { + if resources.get_file_like_as::(fd_in).is_ok() + || resources.get_file_like_as::(fd_out).is_ok() + { return Err(KError::BadFileDescriptor); } @@ -674,10 +666,10 @@ pub fn sys_splice( if off_in.read_vm()? < 0 { return Err(KError::InvalidInput); } - SendFile::Offset(File::from_fd(&fd_table, fd_in)?, off_in.cast()) + SendFile::Offset(resources.get_file_like_as::(fd_in)?, off_in.cast()) } else { // Try to use as pipe first - if let Ok(src) = Pipe::from_fd(&fd_table, fd_in) { + if let Ok(src) = resources.get_file_like_as::(fd_in) { // Pipe must be readable if !src.is_read() { return Err(KError::BadFileDescriptor); @@ -685,12 +677,12 @@ pub fn sys_splice( has_pipe = true; } // Path-only files (opened without O_RDWR/O_WRONLY) cannot be spliced - if let Ok(file) = File::from_fd(&fd_table, fd_in) + if let Ok(file) = resources.get_file_like_as::(fd_in) && file.inner().is_path() { return Err(KError::InvalidInput); } - SendFile::Direct(get_file_like(&fd_table, fd_in)?) + SendFile::Direct(resources.get_file_like(fd_in)?) }; // Setup destination: either with fixed offset or using current position @@ -699,10 +691,10 @@ pub fn sys_splice( if off_out.read_vm()? < 0 { return Err(KError::InvalidInput); } - SendFile::Offset(File::from_fd(&fd_table, fd_out)?, off_out.cast()) + SendFile::Offset(resources.get_file_like_as::(fd_out)?, off_out.cast()) } else { // Try to use as pipe first - if let Ok(dst) = Pipe::from_fd(&fd_table, fd_out) { + if let Ok(dst) = resources.get_file_like_as::(fd_out) { // Pipe must be writable if !dst.is_write() { return Err(KError::BadFileDescriptor); @@ -710,13 +702,13 @@ pub fn sys_splice( has_pipe = true; } // APPEND mode files cannot be spliced (offset cannot be changed) - if let Ok(file) = File::from_fd(&fd_table, fd_out) + if let Ok(file) = resources.get_file_like_as::(fd_out) && file.inner().access(FileFlags::APPEND).is_ok() { return Err(KError::InvalidInput); } // Verify destination is writable with a write probe - let f = get_file_like(&fd_table, fd_out)?; + let f = resources.get_file_like(fd_out)?; f.write(&mut b"".as_slice())?; SendFile::Direct(f) }; diff --git a/posix/fs/src/open.rs b/posix/fs/src/open.rs index 2cfd5cbd..2e4a04eb 100644 --- a/posix/fs/src/open.rs +++ b/posix/fs/src/open.rs @@ -12,7 +12,7 @@ use kcore::vfs::Device; use kerrno::{KError, KResult}; use kfs::{FS_CONTEXT, FileBackend, OpenOptions, OpenResult}; use kservices::{ - file::{Directory, File, FileLike, add_file_like}, + file::{Directory, File, FileLike}, vfs::dev::tty, }; use kthread::current_process_state; @@ -105,10 +105,9 @@ fn add_to_fd(result: OpenResult, flags: u32) -> KResult { if flags & O_NONBLOCK != 0 { f.set_nonblocking(true)?; } - let proc_state = current_process_state(); - let fd_table = proc_state.resources.fd_table(); - let max_nofile = proc_state.resources.max_nofile(); - add_file_like(&fd_table, max_nofile, f, flags & O_CLOEXEC != 0) + current_process_state() + .resources + .add_file_like(f, flags & O_CLOEXEC != 0) } /// Opens a file relative to a directory file descriptor. diff --git a/posix/fs/src/path.rs b/posix/fs/src/path.rs index 7b41be7a..3454a435 100644 --- a/posix/fs/src/path.rs +++ b/posix/fs/src/path.rs @@ -9,7 +9,7 @@ use core::ffi::c_int; use fs_ng_vfs::{Location, Metadata}; use kerrno::{KError, KResult}; -use kfd::{FileLike, Kstat, get_file_like}; +use kfd::{FileLike, Kstat}; use kfs::{FS_CONTEXT, FsContext}; use kservices::file::{Directory, File}; use kthread::{current_process_state, current_thread, get_process_state}; @@ -118,8 +118,8 @@ pub fn with_fs(dirfd: c_int, f: impl FnOnce(&mut FsContext) -> KResult) -> f(&mut fs) } else { let proc_state = current_process_state(); - let fd_table = proc_state.resources.fd_table(); - let dir = Directory::from_fd(&fd_table, dirfd)?.inner().clone(); + let dir = proc_state.resources.get_file_like_as::(dirfd)?; + let dir = dir.inner().clone(); f(&mut fs.with_current_dir(dir)?) } } @@ -231,8 +231,7 @@ fn resolve_empty_path(dirfd: c_int, flags: u32) -> KResult { return Err(KError::NotFound); } let proc_state = current_process_state(); - let fd_table = proc_state.resources.fd_table(); - let file_like = get_file_like(&fd_table, dirfd)?; + let file_like = proc_state.resources.get_file_like(dirfd)?; ResolvedPath::from_file_like(file_like.path().into_owned(), file_like).map(PathSource::Resolved) } @@ -320,7 +319,7 @@ fn procfd_entry(pid: u32, fd: c_int) -> KResult> { .fd_table() .read() .get(fd as usize) - .map(|entry| entry.inner.clone()) + .map(|entry| entry.inner().clone()) .ok_or(KError::BadFileDescriptor) } diff --git a/posix/fs/src/pipe.rs b/posix/fs/src/pipe.rs index a8b78f7d..e7ac5a71 100644 --- a/posix/fs/src/pipe.rs +++ b/posix/fs/src/pipe.rs @@ -8,11 +8,12 @@ //! - Pipe creation (pipe, pipe2, etc.) //! - Pipe flags and configuration (O_CLOEXEC, O_NONBLOCK, etc.) +use alloc::sync::Arc; use core::ffi::c_int; use bitflags::bitflags; use kerrno::KResult; -use kfd::{FileLike, close_file_like}; +use kfd::FileLike; use kservices::file::Pipe; use linux_raw_sys::general::{O_CLOEXEC, O_NONBLOCK}; use osvm::VirtMutPtr; @@ -39,19 +40,17 @@ pub fn sys_pipe2(fds: UserPtr<[c_int; 2]>, flags: u32) -> KResult { new_flags }; - let proc_state = kthread::current_process_state(); - let fd_table = proc_state.resources.fd_table(); - let max_nofile = proc_state.resources.max_nofile(); let cloexec = flags.contains(PipeFlags::CLOEXEC); let (read_end, write_end) = Pipe::new(); if flags.contains(PipeFlags::NONBLOCK) { read_end.set_nonblocking(true)?; write_end.set_nonblocking(true)?; } - let read_fd = read_end.add_to_fd_table(&fd_table, max_nofile, cloexec)?; - let write_fd = write_end - .add_to_fd_table(&fd_table, max_nofile, cloexec) - .inspect_err(|_| close_file_like(&fd_table, read_fd).unwrap())?; + let resources = kthread::current_resources(); + let read_fd = resources.add_file_like(Arc::new(read_end), cloexec)?; + let write_fd = resources + .add_file_like(Arc::new(write_end), cloexec) + .inspect_err(|_| resources.close_file_like(read_fd).unwrap())?; fds.write_vm([read_fd, write_fd])?; diff --git a/posix/fs/src/stat.rs b/posix/fs/src/stat.rs index aadfee4c..856f8803 100644 --- a/posix/fs/src/stat.rs +++ b/posix/fs/src/stat.rs @@ -8,7 +8,6 @@ use core::ffi::{c_char, c_int}; use fs_ng_vfs::{Location, NodePermission}; use kerrno::{KError, KResult}; -use kfd::FileLike; use kfs::FS_CONTEXT; use kservices::file::File; use kthread::current_process_state; @@ -162,7 +161,7 @@ pub fn sys_fstatfs(fd: i32, buf: UserPtr) -> KResult { debug!("sys_fstatfs <= fd: {fd}"); let proc_state = current_process_state(); - let fd_table = proc_state.resources.fd_table(); - buf.write_vm(statfs(File::from_fd(&fd_table, fd)?.inner().location())?)?; + let file = proc_state.resources.get_file_like_as::(fd)?; + buf.write_vm(statfs(file.inner().location())?)?; Ok(0) } diff --git a/posix/mm/src/memfd.rs b/posix/mm/src/memfd.rs index 9f21877b..79f819ae 100644 --- a/posix/mm/src/memfd.rs +++ b/posix/mm/src/memfd.rs @@ -4,12 +4,12 @@ //! Memory file descriptor syscalls. -use alloc::format; +use alloc::{format, sync::Arc}; use core::ffi::c_char; use kerrno::{KError, KResult}; use kfs::{FS_CONTEXT, OpenOptions}; -use kservices::file::{File, FileLike}; +use kservices::file::File; use kthread::current_process_state; use linux_raw_sys::general::{MFD_CLOEXEC, O_RDWR}; use posix_types::UserConstPtr; @@ -31,10 +31,9 @@ pub fn sys_memfd_create(_name: UserConstPtr, flags: u32) -> KResult 0 { - let fd_table = proc_state.resources.fd_table(); - Some(File::from_fd(&fd_table, fd)?) + Some(proc_state.resources.get_file_like_as::(fd)?) } else { None }; diff --git a/process/kfd/src/fd_table.rs b/process/kfd/src/fd_table.rs new file mode 100644 index 00000000..cc62b410 --- /dev/null +++ b/process/kfd/src/fd_table.rs @@ -0,0 +1,217 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2025 KylinSoft Co., Ltd. +// See LICENSES for license details. + +//! File descriptor table ownership and operations. + +use alloc::{sync::Arc, vec::Vec}; +use core::ffi::c_int; + +use downcast_rs::DowncastSync; +use flatten_objects::FlattenObjects; +use kerrno::{KError, KResult}; +use ksync::RwLock; + +use crate::{FileDescriptor, FileLike}; + +/// Process-local file descriptor table. +pub struct FdTable { + entries: FlattenObjects, +} + +impl Default for FdTable { + fn default() -> Self { + Self { + entries: FlattenObjects::new(), + } + } +} + +impl FdTable { + /// Creates a shared descriptor table handle. + pub fn new_shared() -> Arc> { + Arc::new(RwLock::new(Self::default())) + } + + /// Clones a shared descriptor table handle with copied contents. + pub fn clone_shared_from(source: &Arc>) -> Arc> { + let source = source.read(); + let mut cloned = Self::default(); + cloned.clone_from(&source); + Arc::new(RwLock::new(cloned)) + } + + /// Returns the number of occupied descriptors. + pub fn count(&self) -> usize { + self.entries.count() + } + + /// Returns an iterator over allocated descriptor numbers. + pub fn ids(&self) -> impl DoubleEndedIterator + '_ { + self.entries.ids() + } + + /// Returns the descriptor entry at the given index. + pub fn get(&self, fd: usize) -> Option<&FileDescriptor> { + self.entries.get(fd) + } + + /// Returns a mutable descriptor entry at the given index. + pub fn get_mut(&mut self, fd: usize) -> Option<&mut FileDescriptor> { + self.entries.get_mut(fd) + } + + /// Inserts a descriptor entry at the first available slot. + pub fn add(&mut self, descriptor: FileDescriptor) -> Result { + self.entries.add(descriptor) + } + + /// Inserts a descriptor entry at a fixed slot. + pub fn add_at( + &mut self, + fd: usize, + descriptor: FileDescriptor, + ) -> Result { + self.entries.add_at(fd, descriptor) + } + + /// Removes the descriptor entry at the given index. + pub fn remove(&mut self, fd: usize) -> Option { + self.entries.remove(fd) + } + + /// Clones the table contents from another descriptor table. + pub fn clone_from(&mut self, other: &Self) { + self.entries.clone_from(&other.entries); + } + + /// Returns the file-like object stored in the given descriptor. + pub fn get_file_like(&self, fd: c_int) -> KResult> { + self.get(fd as usize) + .map(|descriptor| descriptor.inner().clone()) + .ok_or(KError::BadFileDescriptor) + } + + /// Returns the typed file-like object stored in the given descriptor. + pub fn get_file_like_as(&self, fd: c_int) -> KResult> + where + T: FileLike + DowncastSync + 'static, + { + self.get_file_like(fd)? + .downcast_arc() + .map_err(|_| KError::InvalidInput) + } + + /// Adds a file-like object while enforcing the process soft limit. + pub fn add_file_like( + &mut self, + max_nofile: u64, + file_like: Arc, + cloexec: bool, + ) -> KResult { + if self.count() as u64 >= max_nofile { + return Err(KError::TooManyOpenFiles); + } + + self.add(FileDescriptor::new(file_like, cloexec)) + .map(|fd| fd as c_int) + .map_err(|_| KError::TooManyOpenFiles) + } + + /// Inserts a file-like object without applying a resource-limit policy. + pub fn insert_file_like( + &mut self, + file_like: Arc, + cloexec: bool, + ) -> Result { + self.add(FileDescriptor::new(file_like, cloexec)) + } + + /// Closes a descriptor and removes it from the table. + pub fn close_file_like(&mut self, fd: c_int) -> KResult { + self.remove(fd as usize).ok_or(KError::BadFileDescriptor)?; + Ok(()) + } + + /// Returns the close-on-exec bit for the given descriptor. + pub fn cloexec(&self, fd: c_int) -> KResult { + Ok(self + .get(fd as usize) + .ok_or(KError::BadFileDescriptor)? + .cloexec()) + } + + /// Updates the close-on-exec bit for the given descriptor. + pub fn set_cloexec(&mut self, fd: c_int, cloexec: bool) -> KResult { + self.get_mut(fd as usize) + .ok_or(KError::BadFileDescriptor)? + .set_cloexec(cloexec); + Ok(()) + } + + /// Duplicates one descriptor into a fixed target slot. + pub fn duplicate_to(&mut self, old_fd: c_int, new_fd: c_int, cloexec: bool) -> KResult { + let mut descriptor = self + .get(old_fd as usize) + .cloned() + .ok_or(KError::BadFileDescriptor)?; + descriptor.set_cloexec(cloexec); + + self.remove(new_fd as usize); + self.add_at(new_fd as usize, descriptor) + .map(|_| new_fd) + .map_err(|_| KError::BadFileDescriptor) + } + + /// Closes all descriptors in the given inclusive range. + pub fn close_range(&mut self, first_fd: c_int, last_fd: c_int) { + let max_index = self.ids().next_back(); + if let Some(max_index) = max_index { + for fd in first_fd..=last_fd.min(max_index as c_int) { + self.remove(fd as usize); + } + } + } + + /// Marks all descriptors in the given inclusive range close-on-exec. + pub fn set_cloexec_range(&mut self, first_fd: c_int, last_fd: c_int) { + let max_index = self.ids().next_back(); + if let Some(max_index) = max_index { + for fd in first_fd..=last_fd.min(max_index as c_int) { + if let Some(descriptor) = self.get_mut(fd as usize) { + descriptor.set_cloexec(true); + } + } + } + } + + /// Closes every descriptor currently marked close-on-exec. + pub fn close_cloexec_files(&mut self) { + let cloexec_fds = self + .ids() + .filter(|fd| self.get(*fd).is_some_and(FileDescriptor::cloexec)) + .collect::>(); + + for fd in cloexec_fds { + self.remove(fd); + } + } + + /// Closes all descriptors when the table is not shared. + pub fn close_all_if_unshared(fd_table: &Arc>) { + if Arc::strong_count(fd_table) > 1 { + return; + } + + let mut table = fd_table.write(); + let ids: Vec = table.ids().collect(); + let mut removed = Vec::with_capacity(ids.len()); + for id in ids { + if let Some(descriptor) = table.remove(id) { + removed.push(descriptor); + } + } + drop(table); + drop(removed); + } +} diff --git a/process/kfd/src/file_descriptor.rs b/process/kfd/src/file_descriptor.rs new file mode 100644 index 00000000..dd3eaa39 --- /dev/null +++ b/process/kfd/src/file_descriptor.rs @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2025 KylinSoft Co., Ltd. +// See LICENSES for license details. + +//! File descriptor entries stored in an [`FdTable`](crate::FdTable). + +use alloc::sync::Arc; + +use crate::FileLike; + +/// A file descriptor entry in the file descriptor table. +#[derive(Clone)] +pub struct FileDescriptor { + inner: Arc, + cloexec: bool, +} + +impl FileDescriptor { + /// Creates a new descriptor entry. + pub fn new(inner: Arc, cloexec: bool) -> Self { + Self { inner, cloexec } + } + + /// Returns the underlying file-like object. + pub fn inner(&self) -> &Arc { + &self.inner + } + + /// Returns whether this descriptor is marked close-on-exec. + pub fn cloexec(&self) -> bool { + self.cloexec + } + + /// Updates the close-on-exec bit for this descriptor. + pub fn set_cloexec(&mut self, cloexec: bool) { + self.cloexec = cloexec; + } +} diff --git a/process/kfd/src/file_like.rs b/process/kfd/src/file_like.rs new file mode 100644 index 00000000..91f76315 --- /dev/null +++ b/process/kfd/src/file_like.rs @@ -0,0 +1,101 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2025 KylinSoft Co., Ltd. +// See LICENSES for license details. + +//! File-like trait surface shared by descriptor-backed objects. + +use alloc::{borrow::Cow, sync::Arc}; +use core::ffi::c_int; + +use downcast_rs::{DowncastSync, impl_downcast}; +use kerrno::{KError, KResult}; +use kio::prelude::*; +use kpoll::Pollable; +use ksync::RwLock; + +use crate::{FdTable, Kstat}; + +/// Trait for types that can be used as write destinations in I/O operations. +pub trait WriteBuf: Write + IoBufMut {} +impl WriteBuf for T {} + +/// I/O destination buffer type for write operations. +pub type IoDst<'a> = dyn WriteBuf + 'a; + +/// Trait for types that can be used as read sources in I/O operations. +pub trait ReadBuf: Read + IoBuf {} +impl ReadBuf for T {} + +/// I/O source buffer type for read operations. +pub type IoSrc<'a> = dyn ReadBuf + 'a; + +/// Trait for file-like objects that support standard file operations. +#[allow(dead_code)] +pub trait FileLike: Pollable + DowncastSync { + fn read(&self, _dst: &mut IoDst) -> KResult { + Err(KError::InvalidInput) + } + + fn write(&self, _src: &mut IoSrc) -> KResult { + Err(KError::InvalidInput) + } + + fn stat(&self) -> KResult { + Ok(Kstat::default()) + } + + fn path(&self) -> Cow<'_, str>; + + fn ioctl(&self, _cmd: u32, _arg: usize) -> KResult { + Err(KError::NotATty) + } + + fn open_flags(&self) -> u32 { + 0 + } + + fn nonblocking(&self) -> bool { + false + } + + fn set_nonblocking(&self, _nonblocking: bool) -> KResult { + Ok(()) + } + + /// Returns a typed descriptor entry from a specific descriptor table. + /// + /// This is a low-level helper for resource-owner code and cross-process paths. + /// Current-context callers should go through `ProcessResources` instead of + /// passing raw descriptor-table handles around. + fn from_fd(fd_table: &RwLock, fd: c_int) -> KResult> + where + Self: Sized + 'static, + { + fd_table + .read() + .get_file_like(fd)? + .downcast_arc() + .map_err(|_| KError::InvalidInput) + } + + /// Installs `self` into a specific descriptor table. + /// + /// This is a low-level helper for resource-owner code and explicit + /// cross-process installation paths. Current-context callers should use + /// `ProcessResources::add_file_like`. + fn add_to_fd_table( + self, + fd_table: &RwLock, + max_nofile: u64, + cloexec: bool, + ) -> KResult + where + Self: Sized + 'static, + { + fd_table + .write() + .add_file_like(max_nofile, Arc::new(self), cloexec) + } +} + +impl_downcast!(sync FileLike); diff --git a/process/kfd/src/lib.rs b/process/kfd/src/lib.rs index 939bd97f..ad1781c3 100644 --- a/process/kfd/src/lib.rs +++ b/process/kfd/src/lib.rs @@ -6,285 +6,35 @@ #![no_std] +extern crate alloc; + macro_rules! __log_prefix { () => { "kfd: " }; } -extern crate alloc; +mod fd_table; +mod file_descriptor; +mod file_like; +mod stat; -use alloc::{borrow::Cow, sync::Arc, vec::Vec}; -use core::{ffi::c_int, time::Duration}; - -use downcast_rs::{DowncastSync, impl_downcast}; -use flatten_objects::FlattenObjects; -use fs_ng_vfs::DeviceId; -use kerrno::{KError, KResult}; -use kio::prelude::*; -use kpoll::Pollable; -use ksync::RwLock; -use linux_raw_sys::general::{ - S_IFMT, S_IFREG, STATX_ATTR_WRITE_ATOMIC, STATX_WRITE_ATOMIC, stat, statx, statx_timestamp, +pub use self::{ + fd_table::FdTable, + file_descriptor::FileDescriptor, + file_like::{FileLike, IoDst, IoSrc, ReadBuf, WriteBuf}, + stat::Kstat, }; -/// Kernel stat structure containing file metadata. -#[derive(Debug, Clone, Copy)] -pub struct Kstat { - pub dev: u64, - pub ino: u64, - pub nlink: u32, - pub mode: u32, - pub uid: u32, - pub gid: u32, - pub size: u64, - pub blksize: u32, - pub blocks: u64, - pub rdev: DeviceId, - pub atime: Duration, - pub mtime: Duration, - pub ctime: Duration, -} - -impl Default for Kstat { - fn default() -> Self { - Self { - dev: 0, - ino: 1, - nlink: 1, - mode: 0, - uid: 1, - gid: 1, - size: 0, - blksize: 4096, - blocks: 0, - rdev: DeviceId::default(), - atime: Duration::default(), - mtime: Duration::default(), - ctime: Duration::default(), - } - } -} - -impl From for stat { - fn from(value: Kstat) -> Self { - // SAFETY: `stat` is a POD (plain-old-data) struct from `linux_raw_sys`. All-zeroes - // is a valid initial state for every field (integers become 0, pointers become null). - let mut stat: stat = unsafe { core::mem::zeroed() }; - stat.st_dev = value.dev as _; - stat.st_ino = value.ino as _; - stat.st_nlink = value.nlink as _; - stat.st_mode = value.mode as _; - stat.st_uid = value.uid as _; - stat.st_gid = value.gid as _; - stat.st_size = value.size as _; - stat.st_blksize = value.blksize as _; - stat.st_blocks = value.blocks as _; - stat.st_rdev = value.rdev.0 as _; - - stat.st_atime = value.atime.as_secs() as _; - stat.st_atime_nsec = value.atime.subsec_nanos() as _; - stat.st_mtime = value.mtime.as_secs() as _; - stat.st_mtime_nsec = value.mtime.subsec_nanos() as _; - stat.st_ctime = value.ctime.as_secs() as _; - stat.st_ctime_nsec = value.ctime.subsec_nanos() as _; - stat - } -} - -impl From for statx { - fn from(value: Kstat) -> Self { - const ATOMIC_WRITE_UNIT: u32 = 4096; - - // SAFETY: `statx` is a POD struct from `linux_raw_sys`. All-zeroes is a valid - // initial state for every field (integers become 0, reserved fields become 0). - let mut statx: statx = unsafe { core::mem::zeroed() }; - statx.stx_blksize = value.blksize as _; - statx.stx_attributes = 0; - statx.stx_attributes_mask = STATX_ATTR_WRITE_ATOMIC as _; - statx.stx_nlink = value.nlink as _; - statx.stx_uid = value.uid as _; - statx.stx_gid = value.gid as _; - statx.stx_mode = value.mode as _; - statx.stx_ino = value.ino as _; - statx.stx_size = value.size as _; - statx.stx_blocks = value.blocks as _; - statx.stx_rdev_major = value.rdev.major(); - statx.stx_rdev_minor = value.rdev.minor(); - - fn time_to_statx(time: &Duration) -> statx_timestamp { - statx_timestamp { - tv_sec: time.as_secs() as _, - tv_nsec: time.subsec_nanos() as _, - __reserved: 0, - } - } - statx.stx_atime = time_to_statx(&value.atime); - statx.stx_ctime = time_to_statx(&value.ctime); - statx.stx_mtime = time_to_statx(&value.mtime); - - statx.stx_dev_major = (value.dev >> 32) as _; - statx.stx_dev_minor = value.dev as _; - - if value.mode & S_IFMT == S_IFREG { - statx.stx_attributes |= STATX_ATTR_WRITE_ATOMIC as u64; - statx.stx_atomic_write_unit_min = ATOMIC_WRITE_UNIT; - statx.stx_atomic_write_unit_max = ATOMIC_WRITE_UNIT; - statx.stx_atomic_write_unit_max_opt = ATOMIC_WRITE_UNIT; - statx.stx_atomic_write_segments_max = 1; - statx.stx_mask |= STATX_WRITE_ATOMIC; - } - - statx - } -} - -/// Trait for types that can be used as write destinations in I/O operations. -pub trait WriteBuf: Write + IoBufMut {} -impl WriteBuf for T {} -/// I/O destination buffer type for write operations. -pub type IoDst<'a> = dyn WriteBuf + 'a; - -/// Trait for types that can be used as read sources in I/O operations. -pub trait ReadBuf: Read + IoBuf {} -impl ReadBuf for T {} -/// I/O source buffer type for read operations. -pub type IoSrc<'a> = dyn ReadBuf + 'a; - -/// Trait for file-like objects that support standard file operations. -#[allow(dead_code)] -pub trait FileLike: Pollable + DowncastSync { - fn read(&self, _dst: &mut IoDst) -> KResult { - Err(KError::InvalidInput) - } - - fn write(&self, _src: &mut IoSrc) -> KResult { - Err(KError::InvalidInput) - } - - fn stat(&self) -> KResult { - Ok(Kstat::default()) - } - - fn path(&self) -> Cow<'_, str>; - - fn ioctl(&self, _cmd: u32, _arg: usize) -> KResult { - Err(KError::NotATty) - } - - fn open_flags(&self) -> u32 { - 0 - } - - fn nonblocking(&self) -> bool { - false - } - - fn set_nonblocking(&self, _nonblocking: bool) -> KResult { - Ok(()) - } - - fn from_fd(fd_table: &RwLock, fd: c_int) -> KResult> - where - Self: Sized + 'static, - { - get_file_like(fd_table, fd)? - .downcast_arc() - .map_err(|_| KError::InvalidInput) - } - - fn add_to_fd_table( - self, - fd_table: &RwLock, - max_nofile: u64, - cloexec: bool, - ) -> KResult - where - Self: Sized + 'static, - { - add_file_like(fd_table, max_nofile, Arc::new(self), cloexec) - } -} -impl_downcast!(sync FileLike); - -/// A file descriptor entry in the file descriptor table. -#[derive(Clone)] -pub struct FileDescriptor { - pub inner: Arc, - pub cloexec: bool, -} - -/// The current process-local file descriptor table type. -pub type FdTable = FlattenObjects; - -/// Creates a fresh file descriptor table. -pub fn new_fd_table() -> Arc> { - let mut table = Arc::>::new_uninit(); - Arc::get_mut(&mut table) - .expect("newly allocated Arc must be unique") - .write(RwLock::new(FlattenObjects::new())); - // SAFETY: `Arc::get_mut` above confirmed exclusive access to the inner value, and - // `write()` stored a valid `RwLock` instance. The `MaybeUninit` is now - // fully initialized. - unsafe { table.assume_init() } -} - -/// Retrieves a file-like object from the file descriptor table. -pub fn get_file_like(fd_table: &RwLock, fd: c_int) -> KResult> { - fd_table - .read() - .get(fd as usize) - .map(|fd| fd.inner.clone()) - .ok_or(KError::BadFileDescriptor) -} - -/// Adds a file-like object to the file descriptor table. -pub fn add_file_like( - fd_table: &RwLock, - max_nofile: u64, - f: Arc, - cloexec: bool, -) -> KResult { - let mut table = fd_table.write(); - if table.count() as u64 >= max_nofile { - return Err(KError::TooManyOpenFiles); - } - let fd = FileDescriptor { inner: f, cloexec }; - Ok(table.add(fd).map_err(|_| KError::TooManyOpenFiles)? as c_int) -} - -/// Closes a file descriptor and removes it from the file descriptor table. -pub fn close_file_like(fd_table: &RwLock, fd: c_int) -> KResult { - fd_table - .write() - .remove(fd as usize) - .ok_or(KError::BadFileDescriptor)?; - Ok(()) -} - -/// Closes all file descriptors in the table if it is not shared. -pub fn close_all_fds(fd_table: &Arc>) { - if Arc::strong_count(fd_table) > 1 { - return; - } - - let mut table = fd_table.write(); - let ids: Vec = table.ids().collect(); - let mut removed = Vec::with_capacity(ids.len()); - for id in ids { - if let Some(fd) = table.remove(id) { - removed.push(fd); - } - } - drop(table); - drop(removed); -} - #[cfg(unittest)] mod tests { + use core::time::Duration; + + use fs_ng_vfs::DeviceId; + use linux_raw_sys::general::stat; use unittest::def_test; - use super::*; + use crate::Kstat; #[def_test] fn test_kstat_default() { @@ -338,51 +88,4 @@ mod tests { assert_eq!(s.st_ctime, 3000); assert_eq!(s.st_ctime_nsec, 123_456_789); } - - #[def_test] - fn test_kstat_to_statx() { - let kstat = Kstat { - dev: (5u64 << 32) | 10, - ino: 200, - nlink: 2, - mode: 0o644, - uid: 500, - gid: 500, - size: 8192, - blksize: 4096, - blocks: 16, - rdev: DeviceId::default(), - atime: Duration::new(100, 999_999_999), - mtime: Duration::new(200, 0), - ctime: Duration::new(300, 1), - }; - - let sx: statx = kstat.into(); - assert_eq!(sx.stx_ino, 200); - assert_eq!(sx.stx_nlink, 2); - assert_eq!(sx.stx_mode, 0o644); - assert_eq!(sx.stx_uid, 500); - assert_eq!(sx.stx_gid, 500); - assert_eq!(sx.stx_size, 8192); - assert_eq!(sx.stx_blksize, 4096); - assert_eq!(sx.stx_blocks, 16); - assert_eq!(sx.stx_dev_major, 5); - assert_eq!(sx.stx_dev_minor, 10); - assert_eq!(sx.stx_atime.tv_sec, 100); - assert_eq!(sx.stx_atime.tv_nsec, 999_999_999); - assert_eq!(sx.stx_mtime.tv_sec, 200); - assert_eq!(sx.stx_mtime.tv_nsec, 0); - assert_eq!(sx.stx_ctime.tv_sec, 300); - assert_eq!(sx.stx_ctime.tv_nsec, 1); - } - - #[def_test] - fn test_kstat_default_to_stat_zeroed() { - let s: stat = Kstat::default().into(); - assert_eq!(s.st_dev, 0); - assert_eq!(s.st_size, 0); - assert_eq!(s.st_blksize, 4096); - assert_eq!(s.st_ino, 1); - assert_eq!(s.st_nlink, 1); - } } diff --git a/process/kfd/src/stat.rs b/process/kfd/src/stat.rs new file mode 100644 index 00000000..2af68d43 --- /dev/null +++ b/process/kfd/src/stat.rs @@ -0,0 +1,123 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2025 KylinSoft Co., Ltd. +// See LICENSES for license details. + +//! File metadata types shared by file-like objects. + +use core::time::Duration; + +use fs_ng_vfs::DeviceId; +use linux_raw_sys::general::{ + S_IFMT, S_IFREG, STATX_ATTR_WRITE_ATOMIC, STATX_WRITE_ATOMIC, stat, statx, statx_timestamp, +}; + +/// Kernel stat structure containing file metadata. +#[derive(Debug, Clone, Copy)] +pub struct Kstat { + pub dev: u64, + pub ino: u64, + pub nlink: u32, + pub mode: u32, + pub uid: u32, + pub gid: u32, + pub size: u64, + pub blksize: u32, + pub blocks: u64, + pub rdev: DeviceId, + pub atime: Duration, + pub mtime: Duration, + pub ctime: Duration, +} + +impl Default for Kstat { + fn default() -> Self { + Self { + dev: 0, + ino: 1, + nlink: 1, + mode: 0, + uid: 1, + gid: 1, + size: 0, + blksize: 4096, + blocks: 0, + rdev: DeviceId::default(), + atime: Duration::default(), + mtime: Duration::default(), + ctime: Duration::default(), + } + } +} + +impl From for stat { + fn from(value: Kstat) -> Self { + // SAFETY: `stat` is a POD (plain-old-data) struct from `linux_raw_sys`. All-zeroes + // is a valid initial state for every field (integers become 0, pointers become null). + let mut stat: stat = unsafe { core::mem::zeroed() }; + stat.st_dev = value.dev as _; + stat.st_ino = value.ino as _; + stat.st_nlink = value.nlink as _; + stat.st_mode = value.mode as _; + stat.st_uid = value.uid as _; + stat.st_gid = value.gid as _; + stat.st_size = value.size as _; + stat.st_blksize = value.blksize as _; + stat.st_blocks = value.blocks as _; + stat.st_rdev = value.rdev.0 as _; + + stat.st_atime = value.atime.as_secs() as _; + stat.st_atime_nsec = value.atime.subsec_nanos() as _; + stat.st_mtime = value.mtime.as_secs() as _; + stat.st_mtime_nsec = value.mtime.subsec_nanos() as _; + stat.st_ctime = value.ctime.as_secs() as _; + stat.st_ctime_nsec = value.ctime.subsec_nanos() as _; + stat + } +} + +impl From for statx { + fn from(value: Kstat) -> Self { + const ATOMIC_WRITE_UNIT: u32 = 4096; + + // SAFETY: `statx` is a POD struct from `linux_raw_sys`. All-zeroes is a valid + // initial state for every field (integers become 0, reserved fields become 0). + let mut statx: statx = unsafe { core::mem::zeroed() }; + statx.stx_blksize = value.blksize as _; + statx.stx_attributes = 0; + statx.stx_attributes_mask = STATX_ATTR_WRITE_ATOMIC as _; + statx.stx_nlink = value.nlink as _; + statx.stx_uid = value.uid as _; + statx.stx_gid = value.gid as _; + statx.stx_mode = value.mode as _; + statx.stx_ino = value.ino as _; + statx.stx_size = value.size as _; + statx.stx_blocks = value.blocks as _; + statx.stx_rdev_major = value.rdev.major(); + statx.stx_rdev_minor = value.rdev.minor(); + + fn time_to_statx(time: &Duration) -> statx_timestamp { + statx_timestamp { + tv_sec: time.as_secs() as _, + tv_nsec: time.subsec_nanos() as _, + __reserved: 0, + } + } + statx.stx_atime = time_to_statx(&value.atime); + statx.stx_ctime = time_to_statx(&value.ctime); + statx.stx_mtime = time_to_statx(&value.mtime); + + statx.stx_dev_major = (value.dev >> 32) as _; + statx.stx_dev_minor = value.dev as _; + + if value.mode & S_IFMT == S_IFREG { + statx.stx_attributes |= STATX_ATTR_WRITE_ATOMIC as u64; + statx.stx_atomic_write_unit_min = ATOMIC_WRITE_UNIT; + statx.stx_atomic_write_unit_max = ATOMIC_WRITE_UNIT; + statx.stx_atomic_write_unit_max_opt = ATOMIC_WRITE_UNIT; + statx.stx_atomic_write_segments_max = 1; + statx.stx_mask |= STATX_WRITE_ATOMIC; + } + + statx + } +} diff --git a/process/kresources/Cargo.toml b/process/kresources/Cargo.toml index d1b7cc3b..839abe85 100644 --- a/process/kresources/Cargo.toml +++ b/process/kresources/Cargo.toml @@ -11,6 +11,7 @@ documentation.workspace = true [dependencies] kfd.workspace = true +kerrno.workspace = true krlimit.workspace = true ksync.workspace = true linux-raw-sys = { workspace = true, features = ["general"] } diff --git a/process/kresources/src/lib.rs b/process/kresources/src/lib.rs index 62b9d8a2..dcad169e 100644 --- a/process/kresources/src/lib.rs +++ b/process/kresources/src/lib.rs @@ -15,8 +15,9 @@ macro_rules! __log_prefix { extern crate alloc; use alloc::sync::Arc; +use core::ffi::c_int; -use kfd::{FdTable, new_fd_table}; +use kfd::{FdTable, FileLike}; use krlimit::Rlimits; use ksync::RwLock; use linux_raw_sys::general::RLIMIT_NOFILE; @@ -37,7 +38,7 @@ impl ProcessResources { pub fn new(user_stack_size: usize) -> Arc { Arc::new(Self { rlimits: RwLock::new(Rlimits::new(user_stack_size)), - fd_table: RwLock::new(new_fd_table()), + fd_table: RwLock::new(FdTable::new_shared()), }) } @@ -51,6 +52,93 @@ impl ProcessResources { self.fd_table.read().clone() } + fn with_fd_table(&self, access_fn: impl FnOnce(&RwLock) -> R) -> R { + let fd_table = self.fd_table.read(); + access_fn((*fd_table).as_ref()) + } + + /// Returns the file-like object stored in the given descriptor. + pub fn get_file_like(&self, fd: c_int) -> kerrno::KResult> { + self.with_fd_table(|fd_table| fd_table.read().get_file_like(fd)) + } + + /// Returns the typed file-like object stored in the given descriptor. + pub fn get_file_like_as(&self, fd: c_int) -> kerrno::KResult> { + self.with_fd_table(|fd_table| T::from_fd(fd_table, fd)) + } + + /// Adds a file-like object to the current descriptor table. + pub fn add_file_like( + &self, + file_like: Arc, + cloexec: bool, + ) -> kerrno::KResult { + self.with_fd_table(|fd_table| { + fd_table + .write() + .add_file_like(self.max_nofile(), file_like, cloexec) + }) + } + + /// Duplicates a descriptor into a newly allocated slot. + pub fn duplicate_file_like(&self, fd: c_int, cloexec: bool) -> kerrno::KResult { + let file_like = self.get_file_like(fd)?; + self.add_file_like(file_like, cloexec) + } + + /// Duplicates a descriptor into a fixed slot. + pub fn duplicate_file_like_to( + &self, + old_fd: c_int, + new_fd: c_int, + cloexec: bool, + ) -> kerrno::KResult { + self.with_fd_table(|fd_table| fd_table.write().duplicate_to(old_fd, new_fd, cloexec)) + } + + /// Closes the given file descriptor. + pub fn close_file_like(&self, fd: c_int) -> kerrno::KResult { + self.with_fd_table(|fd_table| fd_table.write().close_file_like(fd)) + } + + /// Returns whether the given descriptor is marked close-on-exec. + pub fn cloexec(&self, fd: c_int) -> kerrno::KResult { + self.with_fd_table(|fd_table| fd_table.read().cloexec(fd)) + } + + /// Updates the close-on-exec bit for the given descriptor. + pub fn set_cloexec(&self, fd: c_int, cloexec: bool) -> kerrno::KResult { + self.with_fd_table(|fd_table| fd_table.write().set_cloexec(fd, cloexec)) + } + + /// Closes all descriptors in the given inclusive range. + pub fn close_range(&self, first_fd: c_int, last_fd: c_int) { + self.with_fd_table(|fd_table| fd_table.write().close_range(first_fd, last_fd)); + } + + /// Marks all descriptors in the given inclusive range close-on-exec. + pub fn set_cloexec_range(&self, first_fd: c_int, last_fd: c_int) { + self.with_fd_table(|fd_table| fd_table.write().set_cloexec_range(first_fd, last_fd)); + } + + /// Closes all descriptors marked close-on-exec. + pub fn close_cloexec_files(&self) { + self.with_fd_table(|fd_table| fd_table.write().close_cloexec_files()); + } + + /// Closes all file descriptors when the table is not shared. + pub fn close_all_fds(&self) { + let fd_table = self.fd_table(); + FdTable::close_all_if_unshared(&fd_table); + } + + /// Replaces the current fd table with an unshared clone. + pub fn unshare_fd_table(&self) { + let old_table = self.fd_table(); + let new_table = FdTable::clone_shared_from(&old_table); + self.replace_fd_table(new_table); + } + /// Replaces the file descriptor table handle. pub fn replace_fd_table(&self, table: Arc>) -> Arc> { core::mem::replace(&mut *self.fd_table.write(), table) diff --git a/process/kthread/src/lib.rs b/process/kthread/src/lib.rs index a02dc8a1..3d78e463 100644 --- a/process/kthread/src/lib.rs +++ b/process/kthread/src/lib.rs @@ -18,6 +18,7 @@ macro_rules! __log_prefix { extern crate alloc; +use alloc::sync::Arc; #[macro_use] extern crate klogger; @@ -55,6 +56,11 @@ pub use thread::{ }; pub use timer::{TimeManager, TimerState, spawn_alarm_task}; +/// Returns the current process-owned resources. +pub fn current_resources() -> Arc { + current_process_state().resources.clone() +} + /// Builds a futex key in the context of the current process address space. pub fn current_futex_key(address: usize) -> FutexKey { let proc_state = current_process_state(); -- Gitee From a7da5c1b7b067da61408f1d2c1037b2e3f0bfbd5 Mon Sep 17 00:00:00 2001 From: Weikang Guo Date: Fri, 8 May 2026 16:46:50 +0800 Subject: [PATCH 14/21] remove use scope local Signed-off-by: Weikang Guo --- Cargo.toml | 1 - core/kcore/Cargo.toml | 1 - core/kcore/src/mm.rs | 17 ++++-- core/kservices/Cargo.toml | 1 - core/kservices/src/file/fs.rs | 6 +- core/kservices/src/file/mod.rs | 4 +- core/kservices/src/task.rs | 2 + core/kservices/src/unittest_task.rs | 1 + core/kservices/src/vfs/dev/dice.rs | 5 +- core/kservices/src/vfs/mod.rs | 4 +- core/ksyscall/Cargo.toml | 1 + core/ksyscall/src/sys.rs | 5 +- core/ksyscall/src/task/clone.rs | 33 ++++------ core/ksyscall/src/task/execve.rs | 10 +-- entry/src/entry.rs | 5 +- entry/src/main.rs | 8 +-- fs/kfs/Cargo.toml | 1 - fs/kfs/src/highlevel/fs.rs | 22 ++++--- fs/kfs/src/highlevel/mod.rs | 5 +- fs/kfs/src/lib.rs | 8 ++- fs/procfs/Cargo.toml | 1 - fs/procfs/src/mounts.rs | 5 +- net/knet/Cargo.toml | 1 + net/knet/src/unix.rs | 10 ++- posix/fs/src/ctl.rs | 25 ++++++-- posix/fs/src/io.rs | 4 +- posix/fs/src/mount.rs | 12 +++- posix/fs/src/open.rs | 9 ++- posix/fs/src/path.rs | 18 +----- posix/fs/src/stat.rs | 4 +- posix/mm/src/memfd.rs | 4 +- process/kthread/Cargo.toml | 3 +- process/kthread/src/process_state.rs | 74 +++++++++++++++++++++-- process/kthread/src/runtime_state.rs | 20 +++--- process/kthread/src/thread/task_ext.rs | 29 +-------- tee/tee_kernel/Cargo.toml | 1 - tee/tee_kernel/src/file.rs | 6 +- tee/tee_kernel/src/tee/common/file_ops.rs | 16 ++--- tee/tee_kernel/src/tee/tee_ree_fs.rs | 39 ++++++------ tee/tee_task_iface/src/tasign.rs | 11 +++- 40 files changed, 258 insertions(+), 174 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d338a37e..0ebd66fd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -187,7 +187,6 @@ mbedtls-sys-auto = { version = "2.28", package = "mbedtls-sys-auto-smx", default ] } percpu = "0.2" rust-dice = { version = "0.1", default-features = false } -scope-local = "0.1" slab = { version = "0.4.9", default-features = false } slab_allocator = { version = "0.4", package = "ax_slab_allocator" } smoltcp = { version = "0.12.0", package = "x-smoltcp", default-features = false } diff --git a/core/kcore/Cargo.toml b/core/kcore/Cargo.toml index 23319ffc..ff842128 100644 --- a/core/kcore/Cargo.toml +++ b/core/kcore/Cargo.toml @@ -33,7 +33,6 @@ linkme.workspace = true memaddr.workspace = true ouroboros = { version = "0.18.5", default-features = false } percpu = { workspace = true } -scope-local.workspace = true slab.workspace = true ksignal.workspace = true kthread.workspace = true diff --git a/core/kcore/src/mm.rs b/core/kcore/src/mm.rs index 181eef49..b9378dc3 100644 --- a/core/kcore/src/mm.rs +++ b/core/kcore/src/mm.rs @@ -4,7 +4,7 @@ //! User address space management. -use alloc::{borrow::ToOwned, string::String, vec, vec::Vec}; +use alloc::{borrow::ToOwned, string::String, sync::Arc, vec, vec::Vec}; use core::{ffi::CStr, hint::unlikely, iter, mem::MaybeUninit}; use extern_trait::extern_trait; @@ -12,7 +12,7 @@ use fs_ng_vfs::Location; use kaddr_layout::{USER_SPACE_BASE, USER_SPACE_SIZE}; use kernel_elf_parser::{AuxEntry, ELFHeaders, ELFHeadersBuilder, ELFParser, app_stack_region}; use kerrno::{KError, KResult}; -use kfs::{CachedFile, FS_CONTEXT, FileBackend}; +use kfs::{CachedFile, FileBackend, boot_fs_context}; use khal::{ asm::user_copy, mem::v2p, @@ -187,13 +187,22 @@ struct ElfLoader(LruCache); type LoadResult = Result<(VirtAddr, Vec), Vec>; +fn current_fs_context() -> Arc> { + current() + .try_as_thread() + .map(|thread| thread.proc_state.fs_context().clone()) + // Loader helpers are also used from kernel tasks before any process + // context exists, so they must fall back to the boot-time FS context. + .unwrap_or_else(|| boot_fs_context().clone()) +} + impl ElfLoader { const fn new() -> Self { Self(LruCache::new()) } fn load(&mut self, uspace: &mut AddrSpace, path: &str) -> KResult { - let loc = FS_CONTEXT.lock().resolve(path)?; + let loc = current_fs_context().lock().resolve(path)?; if !self.0.access(|e| e.borrow_cache().location().ptr_eq(&loc)) { match ElfCacheEntry::load(loc)? { @@ -232,7 +241,7 @@ impl ElfLoader { }; let (elf, ldso) = if let Some(ldso) = ldso { - let loc = FS_CONTEXT.lock().resolve(ldso)?; + let loc = current_fs_context().lock().resolve(ldso)?; if !self.0.access(|e| e.borrow_cache().location().ptr_eq(&loc)) { let e = ElfCacheEntry::load(loc)?.map_err(|_| KError::InvalidInput)?; self.0.put(e); diff --git a/core/kservices/Cargo.toml b/core/kservices/Cargo.toml index 082fc5f4..1978d178 100644 --- a/core/kservices/Cargo.toml +++ b/core/kservices/Cargo.toml @@ -69,7 +69,6 @@ rand = { version = "0.9.1", default-features = false, features = [ ] } rand_chacha = { version = "0.3", default-features = false, optional = true } ringbuf = { version = "0.4.8", default-features = false, features = ["alloc"] } -scope-local.workspace = true slab.workspace = true kprocess.workspace = true kthread.workspace = true diff --git a/core/kservices/src/file/fs.rs b/core/kservices/src/file/fs.rs index e217891b..c45c0457 100644 --- a/core/kservices/src/file/fs.rs +++ b/core/kservices/src/file/fs.rs @@ -15,10 +15,11 @@ use core::{ use fs_ng_vfs::{Location, Metadata, NodeFlags}; use kerrno::{KError, KResult}; use kfd::FdTable; -use kfs::{FS_CONTEXT, FsContext}; +use kfs::FsContext; use kpoll::{IoEvents, Pollable}; use ksync::{Mutex, RwLock}; use ktask::future::{block_on, poll_io}; +use kthread::current_process_state; use linux_raw_sys::general::{AT_EMPTY_PATH, AT_FDCWD, AT_SYMLINK_NOFOLLOW}; use super::{FileLike, Kstat}; @@ -29,7 +30,8 @@ use crate::file::{IoDst, IoSrc}; /// If `dirfd` is `AT_FDCWD`, uses the current directory context. /// Otherwise, resolves the directory from the given file descriptor and uses it as the base. pub fn with_fs(dirfd: c_int, f: impl FnOnce(&mut FsContext) -> KResult) -> KResult { - let mut fs = FS_CONTEXT.lock(); + let proc_state = current_process_state(); + let mut fs = proc_state.fs_context().lock(); if dirfd == AT_FDCWD { f(&mut fs) } else { diff --git a/core/kservices/src/file/mod.rs b/core/kservices/src/file/mod.rs index 2072d4a2..3f4046f0 100644 --- a/core/kservices/src/file/mod.rs +++ b/core/kservices/src/file/mod.rs @@ -18,7 +18,7 @@ use alloc::sync::Arc; use kerrno::{KError, KResult}; use kfd::FdTable; pub use kfd::{FileLike, IoDst, IoSrc, Kstat, ReadBuf, WriteBuf}; -use kfs::{FS_CONTEXT, OpenOptions}; +use kfs::{OpenOptions, boot_fs_context}; use linux_raw_sys::general::{O_RDONLY, O_WRONLY}; pub use self::{ @@ -30,7 +30,7 @@ pub use self::{ pub fn add_stdio(fd_table: &mut FdTable) -> KResult<()> { assert_eq!(fd_table.count(), 0); - let cx = FS_CONTEXT.lock(); + let cx = boot_fs_context().lock(); let open = |options: &mut OpenOptions, flags| { KResult::Ok(Arc::new(File::new( options.open(&cx, "/dev/console")?.into_file()?, diff --git a/core/kservices/src/task.rs b/core/kservices/src/task.rs index 83b6af56..a830841d 100644 --- a/core/kservices/src/task.rs +++ b/core/kservices/src/task.rs @@ -218,6 +218,8 @@ pub fn do_exit(exit_code: i32, group_exit: bool) { thr.proc_state.exit_event().wake(); SHM_MANAGER.lock().clear_proc_shm(process.pid()); + #[cfg(feature = "tee")] + thr.proc_state.clear_tee_runtime_private(); } if group_exit && !process.is_group_exited() { process.group_exit(); diff --git a/core/kservices/src/unittest_task.rs b/core/kservices/src/unittest_task.rs index 378d2bdb..a90351e2 100644 --- a/core/kservices/src/unittest_task.rs +++ b/core/kservices/src/unittest_task.rs @@ -59,6 +59,7 @@ impl InstalledTestThread { "[unittest-user]".to_string(), Arc::new(vec![]), aspace, + kfs::new_process_fs_context(), Arc::new(SpinNoIrq::new(SignalActions::default())), None, Credentials::root(), diff --git a/core/kservices/src/vfs/dev/dice.rs b/core/kservices/src/vfs/dev/dice.rs index 8b676b36..2211a457 100644 --- a/core/kservices/src/vfs/dev/dice.rs +++ b/core/kservices/src/vfs/dev/dice.rs @@ -89,12 +89,13 @@ impl DeviceOps for DiceNodeInfo { fn get_process_hash() -> KResult> { use alloc::format; - use kfs::FS_CONTEXT; + use kthread::current_process_state; use mbedtls::hash::{Md, Type}; let pid = kthread::current_thread().pid(); let proc_exe_path = format!("/proc/{}/exe", pid); - let fs = FS_CONTEXT.lock(); + let proc_state = current_process_state(); + let fs = proc_state.fs_context().lock(); let data = fs.read(proc_exe_path).unwrap(); let mut sm3_result = vec![0u8; 32]; diff --git a/core/kservices/src/vfs/mod.rs b/core/kservices/src/vfs/mod.rs index b7f90ac9..2703fd26 100644 --- a/core/kservices/src/vfs/mod.rs +++ b/core/kservices/src/vfs/mod.rs @@ -15,7 +15,7 @@ use fs_ng_vfs::{ }; pub use kcore::vfs::{Device, DeviceOps, DirMapping, SimpleFs}; use kerrno::{LinuxError, LinuxResult}; -use kfs::{FS_CONTEXT, FsContext}; +use kfs::{FsContext, boot_fs_context}; use ktask::KtaskRef; use kthread::AsThread; use procfs::ProcFsHooks; @@ -66,7 +66,7 @@ fn mount_at(fs: &FsContext, path: &str, mount_fs: Filesystem) -> LinuxResult<()> /// Mount all filesystems /// Mount all virtual filesystems (/dev, /tmp, /proc, /sys, etc.) pub fn mount_all() -> LinuxResult<()> { - let fs = FS_CONTEXT.lock(); + let fs = boot_fs_context().lock(); mount_at(&fs, "/dev", dev::new_devfs())?; mount_at( &fs, diff --git a/core/ksyscall/Cargo.toml b/core/ksyscall/Cargo.toml index 70a9c8da..43e3ed1f 100644 --- a/core/ksyscall/Cargo.toml +++ b/core/ksyscall/Cargo.toml @@ -37,6 +37,7 @@ linux-raw-sys = { workspace = true, features = ["ioctl", "loop_device"] } kprocess.workspace = true kthread.workspace = true ksignal.workspace = true +ksync.workspace = true osvm.workspace = true linux_sysno.workspace = true tee_kernel = { workspace = true, optional = true } diff --git a/core/ksyscall/src/sys.rs b/core/ksyscall/src/sys.rs index d35b01be..9166c2db 100644 --- a/core/ksyscall/src/sys.rs +++ b/core/ksyscall/src/sys.rs @@ -13,8 +13,7 @@ use core::ffi::c_char; use kbuild_config::ARCH; use kerrno::KResult; -use kfs::FS_CONTEXT; -use kthread::processes; +use kthread::{current_process_state, processes}; use linux_raw_sys::{ general::{GRND_INSECURE, GRND_NONBLOCK, GRND_RANDOM}, system::{new_utsname, sysinfo}, @@ -86,7 +85,7 @@ pub fn sys_getrandom(buf: *mut u8, len: usize, flags: u32) -> KResult { "/dev/urandom" }; - let f = FS_CONTEXT.lock().resolve(path)?; + let f = current_process_state().fs_context().lock().resolve(path)?; let mut kbuf = alloc::vec![0; len]; let len = f.entry().as_file()?.read_at(&mut kbuf, 0)?; diff --git a/core/ksyscall/src/task/clone.rs b/core/ksyscall/src/task/clone.rs index 3848d712..12591664 100644 --- a/core/ksyscall/src/task/clone.rs +++ b/core/ksyscall/src/task/clone.rs @@ -17,7 +17,6 @@ use bitflags::bitflags; use kcore::mm::copy_from_kernel; use kerrno::{KError, KResult}; use kfd::{FdTable, FileLike}; -use kfs::FS_CONTEXT; use khal::uspace::UserContext; use kprocess::Pid; use kservices::{file::PidFd, task::new_user_task}; @@ -264,11 +263,17 @@ impl CloneRequest { } else { Arc::new(SpinNoIrq::new(old_proc_data.signal.actions.lock().clone())) }; + let fs_context = if self.flags.contains(CloneFlags::FS) { + old_proc_data.fs_context().clone() + } else { + Arc::new(ksync::Mutex::new(old_proc_data.fs_context().lock().clone())) + }; let proc_state = ProcessState::new( proc, old_proc_data.exe_path().read().clone(), old_proc_data.cmdline().read().clone(), aspace, + fs_context, signal_actions, exit_signal, old_proc_data.credentials.read().clone(), @@ -278,25 +283,13 @@ impl CloneRequest { // Inherit heap pointers from parent to ensure child's heap state is consistent after fork proc_state.set_heap_top(old_proc_data.heap_top()); - { - let mut scope = proc_state.scope().write(); - if self.flags.contains(CloneFlags::FILES) { - proc_state - .resources - .replace_fd_table(old_proc_data.resources.fd_table()); - } else { - let fd_table = FdTable::clone_shared_from(&old_proc_data.resources.fd_table()); - proc_state.resources.replace_fd_table(fd_table); - } - - if self.flags.contains(CloneFlags::FS) { - FS_CONTEXT.scope_mut(&mut scope).clone_from(&FS_CONTEXT); - } else { - FS_CONTEXT - .scope_mut(&mut scope) - .lock() - .clone_from(&FS_CONTEXT.lock()); - } + if self.flags.contains(CloneFlags::FILES) { + proc_state + .resources + .replace_fd_table(old_proc_data.resources.fd_table()); + } else { + let fd_table = FdTable::clone_shared_from(&old_proc_data.resources.fd_table()); + proc_state.resources.replace_fd_table(fd_table); } proc_state diff --git a/core/ksyscall/src/task/execve.rs b/core/ksyscall/src/task/execve.rs index 23d679d6..24a1b8c6 100644 --- a/core/ksyscall/src/task/execve.rs +++ b/core/ksyscall/src/task/execve.rs @@ -15,7 +15,6 @@ use core::ffi::c_char; use kaddr_layout::USER_HEAP_BASE; use kcore::mm::load_user_app; use kerrno::{KError, KResult}; -use kfs::FS_CONTEXT; use khal::uspace::UserContext; use kservices::mm::vm_load_string; use ktask::current; @@ -66,17 +65,18 @@ pub fn sys_execve( load_user_app(&mut aspace, Some(path.as_str()), &args, &envs)?; drop(aspace); - let loc = FS_CONTEXT.lock().resolve(&path)?; + let loc = proc_state.fs_context().lock().resolve(&path)?; + let absolute_path = loc.absolute_path()?.to_string(); curr.set_name(loc.name()); - *proc_state.exe_path().write() = loc.absolute_path()?.to_string(); + *proc_state.exe_path().write() = absolute_path.clone(); *proc_state.cmdline().write() = Arc::new(args); #[cfg(feature = "tee")] { proc_state.tee_ta_ctx.write().init_ta_ctx( - loc.absolute_path()?.to_string().as_str(), - tee_task_iface::tasign::get_ta_head_cached(path.as_str())? + absolute_path.as_str(), + tee_task_iface::tasign::get_ta_head_cached(absolute_path.as_str())? .unwrap_or_default() .as_slice(), ); diff --git a/entry/src/entry.rs b/entry/src/entry.rs index 1b4d6764..6aebb316 100644 --- a/entry/src/entry.rs +++ b/entry/src/entry.rs @@ -10,7 +10,7 @@ use alloc::{ use kcore::mm::{copy_from_kernel, load_user_app, new_user_aspace_empty}; use kcred::Credentials; -use kfs::FS_CONTEXT; +use kfs::{boot_fs_context, new_process_fs_context}; use khal::uspace::UserContext; use kprocess::{Pid, Process}; use kservices::{task::new_user_task, vfs::dev::tty::N_TTY}; @@ -27,7 +27,7 @@ pub fn run_initproc(args: &[String], envs: &[String]) -> i32 { }) .expect("Failed to create user address space"); - let loc = FS_CONTEXT + let loc = boot_fs_context() .lock() .resolve(&args[0]) .expect("Failed to resolve executable path"); @@ -56,6 +56,7 @@ pub fn run_initproc(args: &[String], envs: &[String]) -> i32 { path.to_string(), Arc::new(args.to_vec()), Arc::new(Mutex::new(uspace)), + new_process_fs_context(), Arc::default(), None, Credentials::root(), diff --git a/entry/src/main.rs b/entry/src/main.rs index 25b46cc0..f05f4ebd 100644 --- a/entry/src/main.rs +++ b/entry/src/main.rs @@ -41,7 +41,7 @@ pub const CMDLINE: &[&str] = &["/bin/sh", "-c", include_str!("init.sh")]; fn main() { use alloc::{borrow::ToOwned, vec::Vec}; - use kfs::FS_CONTEXT; + use kfs::boot_fs_context; kservices::init(); let args = CMDLINE @@ -54,7 +54,7 @@ fn main() { let exit_code = entry::run_initproc(&args, &envs); info!("Init process exited with code: {exit_code:?}"); - let cx = FS_CONTEXT.lock(); + let cx = boot_fs_context().lock(); cx.root_dir() .unmount_all() .expect("Failed to unmount all filesystems"); @@ -75,7 +75,7 @@ fn main() { kservices::register_unittest_runtime(); { - let cx = kfs::FS_CONTEXT.lock(); + let cx = kfs::boot_fs_context().lock(); let root = cx.root_dir().clone(); let fs_ops = kfs::FsOperations::new(root); @@ -122,7 +122,7 @@ fn main() { if let Err(e) = xcov::capture_coverage(&mut cov) { error!("capture_coverage failed: {:?}", e); } else if !cov.is_empty() { - let cx = kfs::FS_CONTEXT.lock(); + let cx = kfs::boot_fs_context().lock(); let root = cx.root_dir().clone(); let fs_ops = kfs::FsOperations::new(root); diff --git a/fs/kfs/Cargo.toml b/fs/kfs/Cargo.toml index 7ccff36f..d53bb4f6 100644 --- a/fs/kfs/Cargo.toml +++ b/fs/kfs/Cargo.toml @@ -40,7 +40,6 @@ intrusive-collections = "0.9.7" kspin = { workspace = true } log = { workspace = true } lru = "0.16.0" -scope-local = { workspace = true } slab = { version = "0.4.9", default-features = false } unittest = { workspace = true} ktypes = { workspace = true } diff --git a/fs/kfs/src/highlevel/fs.rs b/fs/kfs/src/highlevel/fs.rs index 2722a250..d0bf6305 100644 --- a/fs/kfs/src/highlevel/fs.rs +++ b/fs/kfs/src/highlevel/fs.rs @@ -30,15 +30,19 @@ use crate::fs_operations::FsOperations as FsContextImpl; /// Global root filesystem context initializer. pub static ROOT_FS_CONTEXT: Once = Once::new(); -scope_local::scope_local! { - /// Thread-local filesystem context handle. - pub static FS_CONTEXT: Arc> = - Arc::new(Mutex::new( - ROOT_FS_CONTEXT - .get() - .expect("Root FS context not initialized") - .clone(), - )); +/// Boot-time filesystem context shared before any user process exists. +pub static BOOT_FS_CONTEXT: Once>> = Once::new(); + +/// Returns the boot-time filesystem context. +pub fn boot_fs_context() -> &'static Arc> { + BOOT_FS_CONTEXT + .get() + .expect("Boot FS context not initialized") +} + +/// Creates a new process-owned filesystem context from the boot defaults. +pub fn new_process_fs_context() -> Arc> { + Arc::new(Mutex::new(boot_fs_context().lock().clone())) } /// Directory entry returned by `ReadDir`. diff --git a/fs/kfs/src/highlevel/mod.rs b/fs/kfs/src/highlevel/mod.rs index c8c7c8fe..ade5f7b5 100644 --- a/fs/kfs/src/highlevel/mod.rs +++ b/fs/kfs/src/highlevel/mod.rs @@ -8,4 +8,7 @@ mod fs; pub use file::*; // Re-export the wrapper FsContext for backward compatibility -pub use fs::{FS_CONTEXT, FsContext, ROOT_FS_CONTEXT, ReadDir, ReadDirEntry}; +pub use fs::{ + BOOT_FS_CONTEXT, FsContext, ROOT_FS_CONTEXT, ReadDir, ReadDirEntry, boot_fs_context, + new_process_fs_context, +}; diff --git a/fs/kfs/src/lib.rs b/fs/kfs/src/lib.rs index 85bf5149..61c1fc52 100644 --- a/fs/kfs/src/lib.rs +++ b/fs/kfs/src/lib.rs @@ -15,6 +15,7 @@ extern crate log; #[cfg(feature = "fs9p")] use alloc::borrow::ToOwned; +use alloc::sync::Arc; mod test_path_resolver; mod test_working_context; @@ -27,6 +28,7 @@ use fs_ng_vfs::{ #[cfg(feature = "fs9p")] use kdriver::Virtio9pDevice; use kdriver::{BlockDevice, DeviceContainer, prelude::*}; +use ksync::Mutex; #[cfg(feature = "fat")] mod disk; @@ -69,7 +71,9 @@ pub fn init_filesystems(mut block_devs: DeviceContainer) { info!(" filesystem type: {:?}", fs.name()); let mp = fs_ng_vfs::Mountpoint::new_root(&fs); - ROOT_FS_CONTEXT.call_once(|| FsContext::new(mp.root_location())); + let root_context = FsContext::new(mp.root_location()); + ROOT_FS_CONTEXT.call_once(|| root_context.clone()); + BOOT_FS_CONTEXT.call_once(|| Arc::new(Mutex::new(root_context))); } #[cfg(feature = "fs9p")] @@ -83,7 +87,7 @@ pub fn mount_9pfilesystems(mut virtio_9p_devs: DeviceContainer, let fs = fs::new_9p_filesystem(dev_9p).expect("Failed to initialize filesystem"); info!(" filesystem type: {:?}", fs.name()); - let mut fs_ctx = FS_CONTEXT.lock(); + let mut fs_ctx = boot_fs_context().lock(); ensure_mount_path(&mut fs_ctx, mount_path).expect("Failed to prepare 9P mount path"); fs_ctx .resolve(mount_path) diff --git a/fs/procfs/Cargo.toml b/fs/procfs/Cargo.toml index 67592d13..a8258dd4 100644 --- a/fs/procfs/Cargo.toml +++ b/fs/procfs/Cargo.toml @@ -16,7 +16,6 @@ fs-ng-vfs.workspace = true kaddr_layout.workspace = true kalloc.workspace = true kcore.workspace = true -kfs.workspace = true khal.workspace = true memaddr.workspace = true memspace.workspace = true diff --git a/fs/procfs/src/mounts.rs b/fs/procfs/src/mounts.rs index 735c8180..3b277e75 100644 --- a/fs/procfs/src/mounts.rs +++ b/fs/procfs/src/mounts.rs @@ -11,7 +11,7 @@ use alloc::{ use fs_ng_vfs::{Mountpoint, ST_NOATIME, ST_NODEV, ST_NOEXEC, ST_NOSUID, ST_RDONLY, ST_RELATIME}; use kcore::vfs::SeqIterator; -use kfs::FS_CONTEXT; +use kthread::current_process_state; #[derive(Clone)] pub(crate) struct ProcMountEntry { @@ -187,7 +187,8 @@ fn make_mount_entry(mount: &Arc, parent_id: u64, mount_id: u64) -> P } fn root_mountpoint() -> Arc { - let fs = FS_CONTEXT.lock(); + let proc_state = current_process_state(); + let fs = proc_state.fs_context().lock(); fs.root_dir().mountpoint().clone() } diff --git a/net/knet/Cargo.toml b/net/knet/Cargo.toml index b5d21c79..4d4e0d61 100644 --- a/net/knet/Cargo.toml +++ b/net/knet/Cargo.toml @@ -18,6 +18,7 @@ kdriver = { workspace = true, features = ["net"] } khal = { workspace = true } ksync = { workspace = true } ktask = { workspace = true } +kthread = { workspace = true } kerrno = { workspace = true } kfs = { workspace = true } fs-ng-vfs = { workspace = true } diff --git a/net/knet/src/unix.rs b/net/knet/src/unix.rs index f4fcc8f3..553c99f6 100644 --- a/net/knet/src/unix.rs +++ b/net/knet/src/unix.rs @@ -14,11 +14,12 @@ use enum_dispatch::enum_dispatch; use fs_ng_vfs::NodeType; use hashbrown::HashMap; use kerrno::{KError, KResult}; -use kfs::{FS_CONTEXT, OpenOptions}; +use kfs::OpenOptions; use kio::{IoBuf, Read, Write}; use kpoll::{IoEvents, Pollable}; use ksync::Mutex; use ktask::future::{block_on, interruptible}; +use kthread::current_process_state; use lazy_static::lazy_static; pub use self::{dgram::DgramTransport, stream::StreamTransport}; @@ -99,7 +100,10 @@ pub(crate) fn lookup_bind_entry( } } UnixAddr::Path(path) => { - let loc = FS_CONTEXT.lock().resolve(path.as_ref())?; + let loc = current_process_state() + .fs_context() + .lock() + .resolve(path.as_ref())?; if loc.metadata()?.node_type != NodeType::Socket { return Err(KError::NotASocket); } @@ -126,7 +130,7 @@ fn lookup_or_create_bind_entry( .write(true) .create(true) .node_type(NodeType::Socket) - .open(&FS_CONTEXT.lock(), path.as_ref())? + .open(¤t_process_state().fs_context().lock(), path.as_ref())? .into_location(); if loc.metadata()?.node_type != NodeType::Socket { return Err(KError::NotASocket); diff --git a/posix/fs/src/ctl.rs b/posix/fs/src/ctl.rs index 431090c7..b41a6598 100644 --- a/posix/fs/src/ctl.rs +++ b/posix/fs/src/ctl.rs @@ -21,7 +21,7 @@ use core::{ use fs_ng_vfs::{MetadataUpdate, NodePermission, NodeType, path::Path}; use kerrno::{KError, KResult}; -use kfs::{FS_CONTEXT, FsContext}; +use kfs::FsContext; use khal::time::wall_time; use kservices::file::Directory; use linux_raw_sys::{ @@ -64,7 +64,8 @@ pub fn sys_chdir(path: UserConstPtr) -> KResult { let path = path.load_string()?; debug!("sys_chdir <= path: {path}"); - let mut fs = FS_CONTEXT.lock(); + let proc_state = kthread::current_process_state(); + let mut fs = proc_state.fs_context().lock(); let entry = fs.resolve(path)?; fs.set_current_dir(entry)?; Ok(0) @@ -75,7 +76,10 @@ pub fn sys_fchdir(dirfd: i32) -> KResult { debug!("sys_fchdir <= dirfd: {dirfd}"); let entry = with_fs(dirfd, |fs| Ok(fs.current_dir().clone()))?; - FS_CONTEXT.lock().set_current_dir(entry)?; + kthread::current_process_state() + .fs_context() + .lock() + .set_current_dir(entry)?; Ok(0) } @@ -89,7 +93,8 @@ pub fn sys_chroot(path: UserConstPtr) -> KResult { let path = path.load_string()?; debug!("sys_chroot <= path: {path}"); - let mut fs = FS_CONTEXT.lock(); + let proc_state = kthread::current_process_state(); + let mut fs = proc_state.fs_context().lock(); let loc = fs.resolve(path)?; if loc.node_type() != NodeType::Directory { return Err(KError::NotADirectory); @@ -283,7 +288,11 @@ pub fn sys_getcwd(buf: UserPtr, size: isize) -> KResult { return Ok(0); } - let cwd = FS_CONTEXT.lock().current_dir().absolute_path()?; + let cwd = kthread::current_process_state() + .fs_context() + .lock() + .current_dir() + .absolute_path()?; debug!("sys_getcwd => cwd: {cwd}"); let cwd = CString::new(cwd.as_str()).map_err(|_| KError::InvalidInput)?; @@ -565,7 +574,11 @@ pub fn sys_renameat2( } pub fn sys_sync() -> KResult { - let root = FS_CONTEXT.lock().root_dir().clone(); + let root = kthread::current_process_state() + .fs_context() + .lock() + .root_dir() + .clone(); root.filesystem().flush()?; Ok(0) } diff --git a/posix/fs/src/io.rs b/posix/fs/src/io.rs index 6178c721..d3dde33b 100644 --- a/posix/fs/src/io.rs +++ b/posix/fs/src/io.rs @@ -19,7 +19,7 @@ use core::{ use kerrno::{KError, KResult, LinuxError}; use kfd::FileLike; -use kfs::{FS_CONTEXT, FileFlags, OpenOptions}; +use kfs::{FileFlags, OpenOptions}; use kio::{Seek, SeekFrom}; use kpoll::{IoEvents, Pollable}; use kservices::{ @@ -167,7 +167,7 @@ pub fn sys_truncate(path: UserConstPtr, length: __kernel_off_t) -> KResu } let file = OpenOptions::new() .write(true) - .open(&FS_CONTEXT.lock(), path)? + .open(&kthread::current_process_state().fs_context().lock(), path)? .into_file()?; file.access(FileFlags::WRITE)?.set_len(length as _)?; Ok(0) diff --git a/posix/fs/src/mount.rs b/posix/fs/src/mount.rs index 52ef5e83..10a0c091 100644 --- a/posix/fs/src/mount.rs +++ b/posix/fs/src/mount.rs @@ -8,8 +8,8 @@ use core::ffi::{c_char, c_void}; use fs_ng_vfs::{ST_NOATIME, ST_NODEV, ST_NOEXEC, ST_NOSUID, ST_RDONLY, ST_RELATIME}; use kerrno::{KError, KResult}; -use kfs::FS_CONTEXT; use kservices::vfs::MemoryFs; +use kthread::current_process_state; use posix_types::UserConstPtr; fn mount_flags_from_sys_mount(flags: i32) -> u32 { @@ -56,7 +56,10 @@ pub fn sys_mount( } let fs = MemoryFs::new_with_flags(mount_flags_from_sys_mount(flags)); - let target = FS_CONTEXT.lock().resolve(target)?; + let target = current_process_state() + .fs_context() + .lock() + .resolve(target)?; target.mount(&fs)?; Ok(0) @@ -67,7 +70,10 @@ pub fn sys_umount2(target: UserConstPtr, _flags: i32) -> KResult let target = target.load_string()?; debug!("sys_umount2 <= target: {target:?}"); - let target = FS_CONTEXT.lock().resolve(target)?; + let target = current_process_state() + .fs_context() + .lock() + .resolve(target)?; target.unmount()?; Ok(0) } diff --git a/posix/fs/src/open.rs b/posix/fs/src/open.rs index 2e4a04eb..3a798d27 100644 --- a/posix/fs/src/open.rs +++ b/posix/fs/src/open.rs @@ -10,7 +10,7 @@ use core::ffi::{c_char, c_int}; use fs_ng_vfs::{DirEntry, FileNode, Location, NodeType, Reference}; use kcore::vfs::Device; use kerrno::{KError, KResult}; -use kfs::{FS_CONTEXT, FileBackend, OpenOptions, OpenResult}; +use kfs::{FileBackend, OpenOptions, OpenResult}; use kservices::{ file::{Directory, File, FileLike}, vfs::dev::tty, @@ -71,7 +71,10 @@ fn add_to_fd(result: OpenResult, flags: u32) -> KResult { let inner = device.inner().as_any(); if let Some(ptmx) = inner.downcast_ref::() { let (master, pty_number) = ptmx.create_pty()?; - let pts = FS_CONTEXT.lock().resolve("/dev/pts")?; + let pts = current_process_state() + .fs_context() + .lock() + .resolve("/dev/pts")?; let entry = DirEntry::new_file( FileNode::new(master), NodeType::CharacterDevice, @@ -94,7 +97,7 @@ fn add_to_fd(result: OpenResult, flags: u32) -> KResult { } else { panic!("unknown terminal type") }; - let loc = FS_CONTEXT.lock().resolve(&path)?; + let loc = current_process_state().fs_context().lock().resolve(&path)?; file = kfs::File::new(FileBackend::Direct(loc), file.flags()); } } diff --git a/posix/fs/src/path.rs b/posix/fs/src/path.rs index 3454a435..e5866099 100644 --- a/posix/fs/src/path.rs +++ b/posix/fs/src/path.rs @@ -10,10 +10,11 @@ use core::ffi::c_int; use fs_ng_vfs::{Location, Metadata}; use kerrno::{KError, KResult}; use kfd::{FileLike, Kstat}; -use kfs::{FS_CONTEXT, FsContext}; use kservices::file::{Directory, File}; use kthread::{current_process_state, current_thread, get_process_state}; -use linux_raw_sys::general::{AT_EMPTY_PATH, AT_FDCWD, AT_SYMLINK_NOFOLLOW, O_NOFOLLOW, O_PATH}; +use linux_raw_sys::general::{AT_EMPTY_PATH, AT_SYMLINK_NOFOLLOW, O_NOFOLLOW, O_PATH}; + +pub use kservices::file::with_fs; /// The coarse shape of a path string before any runtime resolution. #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -111,19 +112,6 @@ pub fn classify_procfd_path(path: &str, current_pid: u32) -> ParsedProcFdPath { ParsedProcFdPath::Parsed(ProcFdPath { pid, fd }) } -/// Executes a function with the file system context for the given directory file descriptor. -pub fn with_fs(dirfd: c_int, f: impl FnOnce(&mut FsContext) -> KResult) -> KResult { - let mut fs = FS_CONTEXT.lock(); - if dirfd == AT_FDCWD { - f(&mut fs) - } else { - let proc_state = current_process_state(); - let dir = proc_state.resources.get_file_like_as::(dirfd)?; - let dir = dir.inner().clone(); - f(&mut fs.with_current_dir(dir)?) - } -} - /// Result of resolving a path at a given directory. #[derive(Clone)] pub enum ResolveAtResult { diff --git a/posix/fs/src/stat.rs b/posix/fs/src/stat.rs index 856f8803..74a65256 100644 --- a/posix/fs/src/stat.rs +++ b/posix/fs/src/stat.rs @@ -8,7 +8,6 @@ use core::ffi::{c_char, c_int}; use fs_ng_vfs::{Location, NodePermission}; use kerrno::{KError, KResult}; -use kfs::FS_CONTEXT; use kservices::file::File; use kthread::current_process_state; use linux_raw_sys::general::{ @@ -147,7 +146,8 @@ pub fn sys_statfs(path: UserConstPtr, buf: UserPtr) -> KResult, flags: u32) -> KResult, + + /// The process-local TEE file table. + #[cfg(feature = "tee")] + tee_fd_table: Arc>>>, + + /// The process-local TEE private runtime state. + #[cfg(feature = "tee")] + tee_runtime_private: RwLock>>, } impl ProcessState { @@ -86,6 +100,7 @@ impl ProcessState { exe_path: String, cmdline: Arc>, address_space: Arc>, + fs_context: Arc>, signal_actions: Arc>, exit_signal: Option, credentials: Credentials, @@ -94,11 +109,15 @@ impl ProcessState { #[cfg(feature = "tee")] let tee_ta_ctx = RwLock::new(TeeTaCtx::new(&exe_path)); let posix = ProcessPosixState::new(exe_path, cmdline, exit_signal); - let runtime = ProcessRuntimeState::new(address_space, config.user_heap_base); + let runtime = ProcessRuntimeState::new(address_space, fs_context, config.user_heap_base); Arc::new(Self { proc, #[cfg(feature = "tee")] tee_ta_ctx, + #[cfg(feature = "tee")] + tee_fd_table: Arc::default(), + #[cfg(feature = "tee")] + tee_runtime_private: RwLock::new(None), resources: ProcessResources::new(config.user_stack_size), posix, lifecycle: ProcessLifecycleState::new(), @@ -193,9 +212,9 @@ impl ProcessState { self.runtime.address_space() } - /// Returns the process-local scope. - pub fn scope(&self) -> &RwLock { - self.runtime.scope() + /// Returns the process-owned filesystem context. + pub fn fs_context(&self) -> &Arc> { + self.runtime.fs_context() } /// Returns the top address of the user heap. @@ -217,6 +236,53 @@ impl ProcessState { pub fn child_time_ns(&self) -> (usize, usize) { self.lifecycle.child_time_ns() } + + /// Returns the process-local TEE file table. + #[cfg(feature = "tee")] + pub fn tee_fd_table(&self) -> &Arc>>> { + &self.tee_fd_table + } + + /// Gets the process-local TEE private state if it has been initialized. + #[cfg(feature = "tee")] + pub fn tee_runtime_private(&self) -> Option> + where + T: Any + Send + Sync, + { + let state = self.tee_runtime_private.read().clone()?; + state.downcast::().ok() + } + + /// Gets or initializes the process-local TEE private state. + #[cfg(feature = "tee")] + pub fn get_or_init_tee_runtime_private(&self, init_fn: F) -> KResult> + where + T: Any + Send + Sync, + F: FnOnce() -> T, + { + if let Some(state) = self.tee_runtime_private::() { + return Ok(state); + } + + let state = Arc::new(init_fn()); + let erased: Arc = state.clone(); + let mut slot = self.tee_runtime_private.write(); + if let Some(existing) = slot.as_ref() { + return existing + .clone() + .downcast::() + .map_err(|_| KError::from(kerrno::KErrorKind::BadState)); + } + + *slot = Some(erased); + Ok(state) + } + + /// Clears the process-local TEE private state. + #[cfg(feature = "tee")] + pub fn clear_tee_runtime_private(&self) { + *self.tee_runtime_private.write() = None; + } } pub(super) struct FutexTables { diff --git a/process/kthread/src/runtime_state.rs b/process/kthread/src/runtime_state.rs index 08c046b4..331b5d98 100644 --- a/process/kthread/src/runtime_state.rs +++ b/process/kthread/src/runtime_state.rs @@ -7,23 +7,27 @@ use alloc::sync::Arc; use core::sync::atomic::{AtomicUsize, Ordering}; -use ksync::{Mutex, RwLock}; +use kfs::FsContext; +use ksync::Mutex; use memspace::AddrSpace; -use scope_local::Scope; /// Process runtime state shared by all threads in a process. pub struct ProcessRuntimeState { address_space: Arc>, - scope: RwLock, + fs_context: Arc>, heap_top: AtomicUsize, } impl ProcessRuntimeState { /// Creates a new [`ProcessRuntimeState`]. - pub fn new(address_space: Arc>, user_heap_base: usize) -> Self { + pub fn new( + address_space: Arc>, + fs_context: Arc>, + user_heap_base: usize, + ) -> Self { Self { address_space, - scope: RwLock::new(Scope::new()), + fs_context, heap_top: AtomicUsize::new(user_heap_base), } } @@ -33,9 +37,9 @@ impl ProcessRuntimeState { &self.address_space } - /// Returns the process-local scope. - pub fn scope(&self) -> &RwLock { - &self.scope + /// Returns the process-owned filesystem context. + pub fn fs_context(&self) -> &Arc> { + &self.fs_context } /// Returns the top address of the user heap. diff --git a/process/kthread/src/thread/task_ext.rs b/process/kthread/src/thread/task_ext.rs index 6882cb9e..5caea0e5 100644 --- a/process/kthread/src/thread/task_ext.rs +++ b/process/kthread/src/thread/task_ext.rs @@ -6,7 +6,6 @@ use alloc::boxed::Box; use extern_trait::extern_trait; use ktask::{TaskExt, TaskInner}; -use scope_local::ActiveScope; use super::Thread; @@ -21,32 +20,10 @@ pub trait AsThread { } } -// SAFETY: `Box` is `Send` (thread state is not shared across threads during -// migration) and the `on_enter`/`on_leave` hooks correctly manage scope lifetime. +// SAFETY: `Box` is `Send` because thread state is not shared across +// threads during migration. #[extern_trait] -unsafe impl TaskExt for Box { - fn on_enter(&self) { - let scope = self.proc_state.scope().read(); - // SAFETY: `scope` is a valid read guard obtained from the current thread's - // process state. It remains active and valid for the duration of this thread's - // execution until `on_leave` is called. - unsafe { ActiveScope::set(&scope) }; - // DESIGN: The read guard is intentionally leaked via `forget` so that the - // scope remains active for the entire lifetime of the thread. It is paired - // with `force_unlock_read()` in `on_leave`, which releases the guard without - // running its `Drop` implementation. - core::mem::forget(scope); - } - - fn on_leave(&self) { - ActiveScope::set_global(); - // SAFETY: `ActiveScope::set_global()` (above) has already unset the active - // scope for this thread, so no code path can access the read guard through - // the scope-local mechanism anymore. It is safe to force-unlock the read - // guard that was leaked in `on_enter`. - unsafe { self.proc_state.scope().force_unlock_read() }; - } -} +unsafe impl TaskExt for Box {} impl AsThread for TaskInner { fn try_as_thread(&self) -> Option<&Thread> { diff --git a/tee/tee_kernel/Cargo.toml b/tee/tee_kernel/Cargo.toml index 41aa9d3e..560cd405 100644 --- a/tee/tee_kernel/Cargo.toml +++ b/tee/tee_kernel/Cargo.toml @@ -58,7 +58,6 @@ static_assertions = "1.1.0" memoffset = { version = "0.9", default-features = false } flatten_objects = "0.2.4" rand_chacha = { version = "0.3", default-features = false } -scope-local = { workspace = true } slab = { workspace = true } rand = { version = "0.9.1", default-features = false, features = [ "alloc", diff --git a/tee/tee_kernel/src/file.rs b/tee/tee_kernel/src/file.rs index 4ddeccf1..fb284cf9 100644 --- a/tee/tee_kernel/src/file.rs +++ b/tee/tee_kernel/src/file.rs @@ -6,7 +6,8 @@ use core::ffi::c_int; use fs_ng_vfs::{Location, Metadata}; use kerrno::{KError, KResult}; -use kfs::{FS_CONTEXT, FsContext}; +use kfs::FsContext; +use kthread::current_process_state; use linux_raw_sys::general::{AT_FDCWD, AT_SYMLINK_NOFOLLOW}; pub fn with_fs(dirfd: c_int, f: impl FnOnce(&mut FsContext) -> KResult) -> KResult { @@ -14,7 +15,8 @@ pub fn with_fs(dirfd: c_int, f: impl FnOnce(&mut FsContext) -> KResult) -> return Err(KError::InvalidInput); } - let mut fs = FS_CONTEXT.lock(); + let proc_state = current_process_state(); + let mut fs = proc_state.fs_context().lock(); f(&mut fs) } diff --git a/tee/tee_kernel/src/tee/common/file_ops.rs b/tee/tee_kernel/src/tee/common/file_ops.rs index 0020673b..04fed664 100644 --- a/tee/tee_kernel/src/tee/common/file_ops.rs +++ b/tee/tee_kernel/src/tee/common/file_ops.rs @@ -7,12 +7,11 @@ use core::ffi::c_int; use fs_ng_vfs::{NodePermission, VfsError}; use kerrno::{KError, KResult}; -use kfs::{FS_CONTEXT, File, FileBackend, FileFlags, OpenOptions, OpenResult}; +use kfs::{File, FileBackend, FileFlags, OpenOptions, OpenResult}; use kio::{Seek, SeekFrom}; use ksync::RwLock; use kthread; use linux_raw_sys::general::*; -use scope_local::scope_local; use slab::Slab; use tee_raw_sys::{TEE_ERROR_GENERIC, TEE_ERROR_ITEM_NOT_FOUND}; @@ -26,9 +25,10 @@ pub const FS_OFLAG_DEFAULT: u32 = O_CREAT | O_RDWR | O_SYNC; pub const FS_OFLAG_RW: u32 = O_RDWR | O_SYNC; pub const FS_OFLAG_RW_TRUNC: u32 = O_RDWR | O_TRUNC | O_SYNC; -scope_local::scope_local! { - /// The open objects for TA. - pub static TEE_FD_TABLE: Arc>>> = Arc::default(); +type TeeFdTable = Arc>>>; + +fn current_tee_fd_table() -> TeeFdTable { + kthread::current_process_state().tee_fd_table().clone() } /// Convert open flags to [`OpenOptions`]. @@ -158,7 +158,7 @@ fn add_to_fd(result: OpenResult, _flags: u32) -> KResult { } }; - let fd = TEE_FD_TABLE.write().insert(Arc::new(f)); + let fd = current_tee_fd_table().write().insert(Arc::new(f)); Ok(fd as isize) } @@ -166,7 +166,7 @@ fn with_file(file: &FileVariant, f: F) -> TeeResult where F: FnOnce(&Arc) -> TeeResult, { - let file_arc = TEE_FD_TABLE + let file_arc = current_tee_fd_table() .read() .get(file.fd as usize) .ok_or_else(|| { @@ -365,7 +365,7 @@ impl TeeFileLike for FileVariant { if self.fd < 0 { return Ok(()); // already closed } - TEE_FD_TABLE + current_tee_fd_table() .write() .try_remove(self.fd as usize) .ok_or_else(|| { diff --git a/tee/tee_kernel/src/tee/tee_ree_fs.rs b/tee/tee_kernel/src/tee/tee_ree_fs.rs index c8a24c46..545ca796 100644 --- a/tee/tee_kernel/src/tee/tee_ree_fs.rs +++ b/tee/tee_kernel/src/tee/tee_ree_fs.rs @@ -13,7 +13,6 @@ use alloc::{ use core::{any::Any, ffi::c_uint, fmt::Debug, ptr}; use ksync::{Mutex, RwLock}; -use scope_local::scope_local; use tee_raw_sys::{TEE_STORAGE_PRIVATE, *}; use super::{ @@ -1185,19 +1184,18 @@ fn commit_dirh_writes(dirh: &mut TeeFsDirfileDirh) -> TeeResult { tee_fs_dirfile_commit_writes(dirh, None) } -/// Process level directory handle cache -/// Using scope_local! to implement, the directory handle will be automatically cleaned up when the process exits, solving the fd invalid problem +/// Process-local directory handle cache. /// -/// Different from OP-TEE: -/// - OP-TEE: ree_fs_dirh is a global variable in the TEE kernel, shared by multiple TAs -/// - StarryOS: DIR_HANDLE_MANAGER is a process level variable, each process is independent +/// The cache lives in the owning [`ProcessState`] instead of a global TEE map, +/// so its lifetime matches the process lifetime and stale handles cannot leak +/// across processes. /// -/// This design has the following advantages: -/// 1. The fd and cache life cycle are consistent, and the cache will be automatically cleaned up when the process exits -/// 2. No need to manually call reset -/// 3. Code is more concise +/// Different from OP-TEE: +/// - OP-TEE keeps `ree_fs_dirh` as a TEE-global object shared by multiple TAs. +/// - X-Kernel keeps one cache per process. /// -/// TODO: multi-process support must be implemented +/// This keeps the handle/cache lifecycle aligned with process exit and avoids a +/// manual reset path. pub struct ReeFsDirh { /// Directory handle cache handle: Option>, @@ -1269,23 +1267,24 @@ impl Drop for ReeFsDirh { } } -// 进程级目录句柄管理器 -// 使用 scope_local! 实现进程级存储,进程退出时自动清理 -// 使用 Arc> 与 TEE_FD_TABLE 的设计保持一致 -scope_local! { - /// 进程级目录句柄缓存 - /// 使用 Arc> 保证线程安全,与 TEE_FD_TABLE 设计一致 - pub static DIR_HANDLE_MANAGER: Arc> = Arc::new(Mutex::new(ReeFsDirh::new())); +type DirHandleManager = Arc>; + +fn current_dir_handle_manager() -> TeeResult { + kthread::current_process_state() + .get_or_init_tee_runtime_private(|| Mutex::new(ReeFsDirh::new())) + .map_err(|_| TEE_ERROR_GENERIC) } /// 获取目录句柄 pub fn get_dirh() -> TeeResult<*mut TeeFsDirfileDirh> { - DIR_HANDLE_MANAGER.lock().get_dirh() + current_dir_handle_manager()?.lock().get_dirh() } /// 释放目录句柄引用 pub fn put_dirh_primitive(close: bool) -> TeeResult { - DIR_HANDLE_MANAGER.lock().put_dirh_primitive(close) + current_dir_handle_manager()? + .lock() + .put_dirh_primitive(close) } /// 释放目录句柄 diff --git a/tee/tee_task_iface/src/tasign.rs b/tee/tee_task_iface/src/tasign.rs index 1918e0aa..6a9ecbb9 100644 --- a/tee/tee_task_iface/src/tasign.rs +++ b/tee/tee_task_iface/src/tasign.rs @@ -9,7 +9,7 @@ use core::convert::AsRef; use hashbrown::HashMap; use kerrno::{KError, KResult}; -use kfs::{CachedFile, FS_CONTEXT}; +use kfs::{CachedFile, boot_fs_context}; use log::{error, info}; use spin::{Lazy, Mutex}; use tee_raw_sys::ta_head; @@ -130,9 +130,14 @@ pub fn verify_ta_elf_on_load_and_cache_ta_head(cache: &CachedFile) -> KResult<() Ok(()) } -/// Cache lookup by canonical path; on miss, [`verify_ta_elf_signature_if_applicable`] and store `ta_head`. +/// Cache lookup by canonical absolute path; on miss, verifies the image and +/// stores the resulting `ta_head`. +/// +/// Callers should pass the resolved absolute executable path rather than the +/// raw user-provided exec path so lookup stays independent of per-process cwd +/// or chroot state. pub fn get_ta_head_cached(path: &str) -> KResult>> { - let loc = FS_CONTEXT.lock().resolve(path)?; + let loc = boot_fs_context().lock().resolve(path)?; let abs = loc.absolute_path()?; let key: String = String::from(AsRef::::as_ref(&abs)); -- Gitee From 35af1593bab26e00c6222e29d80bf9fa2cd75592 Mon Sep 17 00:00:00 2001 From: Weikang Guo Date: Fri, 8 May 2026 18:10:55 +0800 Subject: [PATCH 15/21] fix rebase Signed-off-by: Weikang Guo --- core/ksyscall/Cargo.toml | 1 - posix/fs/src/fd_ops.rs | 1 + posix/fs/src/io.rs | 16 +++++++++------- posix/fs/src/path.rs | 3 +-- process/kthread/src/lib.rs | 4 ++-- process/kthread/src/thread/current.rs | 7 ++++++- process/kthread/src/thread/mod.rs | 2 +- 7 files changed, 20 insertions(+), 14 deletions(-) diff --git a/core/ksyscall/Cargo.toml b/core/ksyscall/Cargo.toml index 43e3ed1f..4f7d81d9 100644 --- a/core/ksyscall/Cargo.toml +++ b/core/ksyscall/Cargo.toml @@ -23,7 +23,6 @@ kfd.workspace = true kservices = { workspace = true } kbuild_config = { workspace = true } kerrno.workspace = true -kfs.workspace = true khal.workspace = true kio.workspace = true klogger.workspace = true diff --git a/posix/fs/src/fd_ops.rs b/posix/fs/src/fd_ops.rs index 566a25e1..4bbb243b 100644 --- a/posix/fs/src/fd_ops.rs +++ b/posix/fs/src/fd_ops.rs @@ -15,6 +15,7 @@ use bitflags::bitflags; use kerrno::{KError, KResult}; use kservices::file::Pipe; use linux_raw_sys::general::*; +use osvm::{VirtMutPtr, VirtPtr}; use posix_types::UserPtr; /// Closes the specified file descriptor. diff --git a/posix/fs/src/io.rs b/posix/fs/src/io.rs index d3dde33b..c4081bb1 100644 --- a/posix/fs/src/io.rs +++ b/posix/fs/src/io.rs @@ -27,9 +27,9 @@ use kservices::{ io::{IoVec, IoVectorBuf}, mm::{VmBytes, VmBytesMut}, }; -use ktask::current; use linux_raw_sys::general::__kernel_off_t; use linux_sysno::Sysno; +use osvm::{VirtMutPtr, VirtPtr}; use posix_types::{UserConstPtr, UserPtr}; struct DummyFd; @@ -70,7 +70,7 @@ fn write_zeros_range(file: &kfs::FileBackend, start: u64, end: u64) -> KResult<( /// Creates a dummy file descriptor for unsupported syscalls. pub fn sys_dummy_fd(sysno: Sysno) -> KResult { // Check if running under QEMU - if so, report unsupported to let QEMU fall back to alternatives - if current().name().starts_with("qemu-") { + if kthread::current_task_name().starts_with("qemu-") { // We need to be honest to qemu, since it can automatically fallback to // other strategies. return Err(KError::Unsupported); @@ -89,7 +89,7 @@ pub fn sys_read(fd: i32, buf: UserPtr, len: usize) -> KResult { // Get the file object and perform the read operation into the user buffer Ok(kthread::current_resources() .get_file_like(fd)? - .read(&mut VmBytesMut::new(buf, len))? as _) + .read(&mut VmBytesMut::new(buf.as_ptr().cast_mut(), len))? as _) } /// Vectored read into multiple buffers. @@ -97,7 +97,7 @@ pub fn sys_readv(fd: i32, iov: UserConstPtr, iovcnt: usize) -> KResult, len: usize) -> KResult { debug!("sys_write <= fd: {fd}, buf: {:p}, len: {len}", buf.as_ptr()); Ok(kthread::current_resources() .get_file_like(fd)? - .write(&mut VmBytes::new(buf, len))? as _) + .write(&mut VmBytes::new(buf.as_ptr(), len))? as _) } /// Vectored write from multiple buffers. @@ -116,7 +116,7 @@ pub fn sys_writev(fd: i32, iov: UserConstPtr, iovcnt: usize) -> KResult(fd)?; - let write = f.inner().write_at(VmBytes::new(buf, len), offset as _)?; + let write = f + .inner() + .write_at(VmBytes::new(buf.as_ptr(), len), offset as _)?; Ok(write as _) } diff --git a/posix/fs/src/path.rs b/posix/fs/src/path.rs index e5866099..e2919273 100644 --- a/posix/fs/src/path.rs +++ b/posix/fs/src/path.rs @@ -10,12 +10,11 @@ use core::ffi::c_int; use fs_ng_vfs::{Location, Metadata}; use kerrno::{KError, KResult}; use kfd::{FileLike, Kstat}; +pub use kservices::file::with_fs; use kservices::file::{Directory, File}; use kthread::{current_process_state, current_thread, get_process_state}; use linux_raw_sys::general::{AT_EMPTY_PATH, AT_SYMLINK_NOFOLLOW, O_NOFOLLOW, O_PATH}; -pub use kservices::file::with_fs; - /// The coarse shape of a path string before any runtime resolution. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum PathKind { diff --git a/process/kthread/src/lib.rs b/process/kthread/src/lib.rs index 3d78e463..b4a6ee88 100644 --- a/process/kthread/src/lib.rs +++ b/process/kthread/src/lib.rs @@ -51,8 +51,8 @@ pub use stat::TaskStat; #[cfg(feature = "tee")] pub use tee_task_iface::{TeeSessionCtxTrait, TeeTaCtx}; pub use thread::{ - AsThread, AssumeSync, CurrentThread, Thread, current_process_state, current_thread, - with_current_thread, + AsThread, AssumeSync, CurrentThread, Thread, current_process_state, current_task_name, + current_thread, with_current_thread, }; pub use timer::{TimeManager, TimerState, spawn_alarm_task}; diff --git a/process/kthread/src/thread/current.rs b/process/kthread/src/thread/current.rs index 96d66535..4f8e5d74 100644 --- a/process/kthread/src/thread/current.rs +++ b/process/kthread/src/thread/current.rs @@ -2,7 +2,7 @@ // Copyright 2025 KylinSoft Co., Ltd. // See LICENSES for license details. -use alloc::sync::Arc; +use alloc::{string::String, sync::Arc}; use core::ops::Deref; use ktask::current; @@ -23,6 +23,11 @@ pub fn current_thread() -> CurrentThread { CurrentThread(current().clone()) } +/// Returns the current task name. +pub fn current_task_name() -> String { + current().name() +} + /// Executes a closure with the current user thread. pub fn with_current_thread(f: impl FnOnce(&Thread) -> R) -> R { let thread = current_thread(); diff --git a/process/kthread/src/thread/mod.rs b/process/kthread/src/thread/mod.rs index ea229487..67965452 100644 --- a/process/kthread/src/thread/mod.rs +++ b/process/kthread/src/thread/mod.rs @@ -8,5 +8,5 @@ mod task_ext; pub use core::{AssumeSync, CurrentThread, Thread}; -pub use current::{current_process_state, current_thread, with_current_thread}; +pub use current::{current_process_state, current_task_name, current_thread, with_current_thread}; pub use task_ext::AsThread; -- Gitee From 024d42ea4166e8e68a5ea40d31e1681abbc7c690 Mon Sep 17 00:00:00 2001 From: Weikang Guo Date: Sat, 9 May 2026 09:41:27 +0800 Subject: [PATCH 16/21] Refactor current fs context access Signed-off-by: Weikang Guo --- .../asterinas-coding-guidelines/SKILL.md | 113 ++++++++++++++++++ core/kcore/src/mm.rs | 17 +-- core/kservices/src/file/fs.rs | 8 +- core/kservices/src/file/mod.rs | 8 +- core/kservices/src/vfs/mod.rs | 4 +- core/ksyscall/src/sys.rs | 4 +- entry/src/entry.rs | 7 +- entry/src/main.rs | 8 +- fs/kfs/src/highlevel/fs.rs | 16 +-- fs/kfs/src/highlevel/mod.rs | 2 +- fs/kfs/src/lib.rs | 4 +- net/knet/src/unix.rs | 6 +- posix/fs/src/io.rs | 2 +- posix/fs/src/open.rs | 4 +- posix/mm/src/memfd.rs | 4 +- process/kthread/src/lib.rs | 4 +- process/kthread/src/thread/current.rs | 25 ++++ process/kthread/src/thread/mod.rs | 5 +- tee/tee_kernel/src/file.rs | 7 +- tee/tee_task_iface/src/tasign.rs | 4 +- 20 files changed, 194 insertions(+), 58 deletions(-) diff --git a/.claude/skills/asterinas-coding-guidelines/SKILL.md b/.claude/skills/asterinas-coding-guidelines/SKILL.md index 5ee6c1e9..379aa77b 100644 --- a/.claude/skills/asterinas-coding-guidelines/SKILL.md +++ b/.claude/skills/asterinas-coding-guidelines/SKILL.md @@ -250,6 +250,119 @@ through public APIs (including their documentation). A module's public surface should contain only what its consumers need. +### Encode current-context semantics in APIs (`current-context-semantics`) {#current-context-semantics} + +When code needs a "current" resource, +the API should state whether it requires +a current **process thread** +or only a current **execution path**. +Do not force callers to infer this from hidden assumptions. + +For filesystem context access in x-kernel: + +- Use `kthread::current_process_fs_context()` + for process-only paths + such as syscalls and POSIX helpers. +- Use `kthread::current_fs_context()` + for shared helpers + that may run from either a user thread + or a kernel task. +- Prefer passing `&FsContext` or `Arc>` + into deeper helpers + rather than reading current context implicitly. + +### Use the correct fd abstraction layer (`fd-abstraction-layers`) {#fd-abstraction-layers} + +File descriptor operations use a three-layer architecture. +Each layer has a clear responsibility. +Callers must use the highest layer +that satisfies their need. + +| Layer | Crate | Type | Responsibility | +|-------|-------|------|----------------| +| Low-level | `kfd` | `FdTable` | Table storage, insert/remove, no policy | +| Process | `kresources` | `ProcessResources` | Per-process wrappers with rlimit enforcement | +| Current-context | `kthread` | `current_resources()` | Shortcut for the current process | + +#### Low-level: `kfd::FdTable` + +`FdTable` owns the descriptor storage. +Its methods are thin table operations: +`get`, `add`, `remove`, `add_at`, `clone_from`. +They take `&mut self` and enforce no resource-limit policy. + +Use this layer only when building new process-level wrappers +or when you already hold a direct `&mut FdTable` reference. + +#### Process: `kresources::ProcessResources` + +`ProcessResources` wraps `Arc>` +and exposes named methods that enforce process policy +(rlimit checks, close-on-exec semantics, unsharing). + +```rust +// Good — uses the process layer +let resources = current_resources(); +let file = resources.get_file_like(fd)?; +let new_fd = resources.add_file_like(file, cloexec)?; +resources.close_file_like(fd)?; +``` + +```rust +// Bad — reaches into the fd_table Arc directly +// from code that only cares about the current process +let fd_table = current_process_state().resources.fd_table(); +let file = fd_table.read().get_file_like(fd)?; +``` + +Internal helpers use `with_fd_table` +to avoid cloning the `Arc` for single-access operations: + +```rust +fn with_fd_table(&self, access_fn: impl FnOnce(&RwLock) -> R) -> R { + let fd_table = self.fd_table.read(); + access_fn((*fd_table).as_ref()) +} +``` + +#### Current-context: `kthread::current_resources()` + +For code that operates on the *current* process +(typical in syscall handlers), +use `kthread::current_resources()`. +It returns `Arc` +and avoids the verbose +`current_process_state().resources.clone()` spell. + +```rust +// Good — concise, uses the current-context shortcut +use kthread::current_resources; +let file = current_resources().get_file_like(fd)?; +``` + +#### Type-specific lookups: `get_file_like_as` + +`ProcessResources::get_file_like_as::` delegates +to `T::from_fd(fd_table, fd)` rather than +a generic `downcast`. +This preserves type-specific error codes +(e.g., `NotASocket` instead of a generic `InvalidInput`). + +```rust +// Good — returns NotASocket on mismatch +let socket = current_resources().get_file_like_as::(fd)?; + +// Bad — returns InvalidInput on mismatch +let file = current_resources().get_file_like(fd)?; +let socket = file.downcast_arc::().map_err(|_| KError::InvalidInput)?; +``` + +#### Encapsulation: `FileDescriptor` fields are private + +`FileDescriptor` in `kfd` exposes its fields through getters +(`inner()`, `cloexec()`, `set_cloexec()`). +Direct field access is not permitted outside `kfd`. + ### Validate at boundaries, trust internally (`validate-at-boundaries`) {#validate-at-boundaries} Designate certain interfaces as validation boundaries. diff --git a/core/kcore/src/mm.rs b/core/kcore/src/mm.rs index b9378dc3..9c81f048 100644 --- a/core/kcore/src/mm.rs +++ b/core/kcore/src/mm.rs @@ -4,7 +4,7 @@ //! User address space management. -use alloc::{borrow::ToOwned, string::String, sync::Arc, vec, vec::Vec}; +use alloc::{borrow::ToOwned, string::String, vec, vec::Vec}; use core::{ffi::CStr, hint::unlikely, iter, mem::MaybeUninit}; use extern_trait::extern_trait; @@ -12,7 +12,7 @@ use fs_ng_vfs::Location; use kaddr_layout::{USER_SPACE_BASE, USER_SPACE_SIZE}; use kernel_elf_parser::{AuxEntry, ELFHeaders, ELFHeadersBuilder, ELFParser, app_stack_region}; use kerrno::{KError, KResult}; -use kfs::{CachedFile, FileBackend, boot_fs_context}; +use kfs::{CachedFile, FileBackend}; use khal::{ asm::user_copy, mem::v2p, @@ -187,22 +187,13 @@ struct ElfLoader(LruCache); type LoadResult = Result<(VirtAddr, Vec), Vec>; -fn current_fs_context() -> Arc> { - current() - .try_as_thread() - .map(|thread| thread.proc_state.fs_context().clone()) - // Loader helpers are also used from kernel tasks before any process - // context exists, so they must fall back to the boot-time FS context. - .unwrap_or_else(|| boot_fs_context().clone()) -} - impl ElfLoader { const fn new() -> Self { Self(LruCache::new()) } fn load(&mut self, uspace: &mut AddrSpace, path: &str) -> KResult { - let loc = current_fs_context().lock().resolve(path)?; + let loc = kthread::current_fs_context().lock().resolve(path)?; if !self.0.access(|e| e.borrow_cache().location().ptr_eq(&loc)) { match ElfCacheEntry::load(loc)? { @@ -241,7 +232,7 @@ impl ElfLoader { }; let (elf, ldso) = if let Some(ldso) = ldso { - let loc = current_fs_context().lock().resolve(ldso)?; + let loc = kthread::current_fs_context().lock().resolve(ldso)?; if !self.0.access(|e| e.borrow_cache().location().ptr_eq(&loc)) { let e = ElfCacheEntry::load(loc)?.map_err(|_| KError::InvalidInput)?; self.0.put(e); diff --git a/core/kservices/src/file/fs.rs b/core/kservices/src/file/fs.rs index c45c0457..700369d3 100644 --- a/core/kservices/src/file/fs.rs +++ b/core/kservices/src/file/fs.rs @@ -19,7 +19,6 @@ use kfs::FsContext; use kpoll::{IoEvents, Pollable}; use ksync::{Mutex, RwLock}; use ktask::future::{block_on, poll_io}; -use kthread::current_process_state; use linux_raw_sys::general::{AT_EMPTY_PATH, AT_FDCWD, AT_SYMLINK_NOFOLLOW}; use super::{FileLike, Kstat}; @@ -27,11 +26,14 @@ use crate::file::{IoDst, IoSrc}; /// Executes a function with the file system context for the given directory file descriptor. /// +/// This helper intentionally uses `kthread::current_fs_context()` because it is +/// shared by both syscall paths and kernel-service callers. +/// /// If `dirfd` is `AT_FDCWD`, uses the current directory context. /// Otherwise, resolves the directory from the given file descriptor and uses it as the base. pub fn with_fs(dirfd: c_int, f: impl FnOnce(&mut FsContext) -> KResult) -> KResult { - let proc_state = current_process_state(); - let mut fs = proc_state.fs_context().lock(); + let fs_context = kthread::current_fs_context(); + let mut fs = fs_context.lock(); if dirfd == AT_FDCWD { f(&mut fs) } else { diff --git a/core/kservices/src/file/mod.rs b/core/kservices/src/file/mod.rs index 3f4046f0..91543475 100644 --- a/core/kservices/src/file/mod.rs +++ b/core/kservices/src/file/mod.rs @@ -18,7 +18,7 @@ use alloc::sync::Arc; use kerrno::{KError, KResult}; use kfd::FdTable; pub use kfd::{FileLike, IoDst, IoSrc, Kstat, ReadBuf, WriteBuf}; -use kfs::{OpenOptions, boot_fs_context}; +use kfs::{FsContext, OpenOptions}; use linux_raw_sys::general::{O_RDONLY, O_WRONLY}; pub use self::{ @@ -28,12 +28,12 @@ pub use self::{ pipe::Pipe, }; -pub fn add_stdio(fd_table: &mut FdTable) -> KResult<()> { +/// Adds stdin/stdout/stderr entries using the provided filesystem view. +pub fn add_stdio(fd_table: &mut FdTable, fs_context: &FsContext) -> KResult<()> { assert_eq!(fd_table.count(), 0); - let cx = boot_fs_context().lock(); let open = |options: &mut OpenOptions, flags| { KResult::Ok(Arc::new(File::new( - options.open(&cx, "/dev/console")?.into_file()?, + options.open(fs_context, "/dev/console")?.into_file()?, flags, ))) }; diff --git a/core/kservices/src/vfs/mod.rs b/core/kservices/src/vfs/mod.rs index 2703fd26..1c95c0ae 100644 --- a/core/kservices/src/vfs/mod.rs +++ b/core/kservices/src/vfs/mod.rs @@ -15,7 +15,7 @@ use fs_ng_vfs::{ }; pub use kcore::vfs::{Device, DeviceOps, DirMapping, SimpleFs}; use kerrno::{LinuxError, LinuxResult}; -use kfs::{FsContext, boot_fs_context}; +use kfs::{FsContext, kernel_fs_context}; use ktask::KtaskRef; use kthread::AsThread; use procfs::ProcFsHooks; @@ -66,7 +66,7 @@ fn mount_at(fs: &FsContext, path: &str, mount_fs: Filesystem) -> LinuxResult<()> /// Mount all filesystems /// Mount all virtual filesystems (/dev, /tmp, /proc, /sys, etc.) pub fn mount_all() -> LinuxResult<()> { - let fs = boot_fs_context().lock(); + let fs = kernel_fs_context().lock(); mount_at(&fs, "/dev", dev::new_devfs())?; mount_at( &fs, diff --git a/core/ksyscall/src/sys.rs b/core/ksyscall/src/sys.rs index 9166c2db..aaa536c5 100644 --- a/core/ksyscall/src/sys.rs +++ b/core/ksyscall/src/sys.rs @@ -13,7 +13,7 @@ use core::ffi::c_char; use kbuild_config::ARCH; use kerrno::KResult; -use kthread::{current_process_state, processes}; +use kthread::{current_process_fs_context, processes}; use linux_raw_sys::{ general::{GRND_INSECURE, GRND_NONBLOCK, GRND_RANDOM}, system::{new_utsname, sysinfo}, @@ -85,7 +85,7 @@ pub fn sys_getrandom(buf: *mut u8, len: usize, flags: u32) -> KResult { "/dev/urandom" }; - let f = current_process_state().fs_context().lock().resolve(path)?; + let f = current_process_fs_context().lock().resolve(path)?; let mut kbuf = alloc::vec![0; len]; let len = f.entry().as_file()?.read_at(&mut kbuf, 0)?; diff --git a/entry/src/entry.rs b/entry/src/entry.rs index 6aebb316..37352db9 100644 --- a/entry/src/entry.rs +++ b/entry/src/entry.rs @@ -10,7 +10,7 @@ use alloc::{ use kcore::mm::{copy_from_kernel, load_user_app, new_user_aspace_empty}; use kcred::Credentials; -use kfs::{boot_fs_context, new_process_fs_context}; +use kfs::{kernel_fs_context, new_process_fs_context}; use khal::uspace::UserContext; use kprocess::{Pid, Process}; use kservices::{task::new_user_task, vfs::dev::tty::N_TTY}; @@ -27,7 +27,7 @@ pub fn run_initproc(args: &[String], envs: &[String]) -> i32 { }) .expect("Failed to create user address space"); - let loc = boot_fs_context() + let loc = kernel_fs_context() .lock() .resolve(&args[0]) .expect("Failed to resolve executable path"); @@ -63,7 +63,8 @@ pub fn run_initproc(args: &[String], envs: &[String]) -> i32 { ProcessStateConfig::default(), ); { - kservices::file::add_stdio(&mut proc_state.resources.fd_table().write()) + let fs_context = proc_state.fs_context().lock(); + kservices::file::add_stdio(&mut proc_state.resources.fd_table().write(), &fs_context) .expect("Failed to add stdio"); } let thr = Thread::new(pid, proc_state); diff --git a/entry/src/main.rs b/entry/src/main.rs index f05f4ebd..b3412d28 100644 --- a/entry/src/main.rs +++ b/entry/src/main.rs @@ -41,7 +41,7 @@ pub const CMDLINE: &[&str] = &["/bin/sh", "-c", include_str!("init.sh")]; fn main() { use alloc::{borrow::ToOwned, vec::Vec}; - use kfs::boot_fs_context; + use kfs::kernel_fs_context; kservices::init(); let args = CMDLINE @@ -54,7 +54,7 @@ fn main() { let exit_code = entry::run_initproc(&args, &envs); info!("Init process exited with code: {exit_code:?}"); - let cx = boot_fs_context().lock(); + let cx = kernel_fs_context().lock(); cx.root_dir() .unmount_all() .expect("Failed to unmount all filesystems"); @@ -75,7 +75,7 @@ fn main() { kservices::register_unittest_runtime(); { - let cx = kfs::boot_fs_context().lock(); + let cx = kfs::kernel_fs_context().lock(); let root = cx.root_dir().clone(); let fs_ops = kfs::FsOperations::new(root); @@ -122,7 +122,7 @@ fn main() { if let Err(e) = xcov::capture_coverage(&mut cov) { error!("capture_coverage failed: {:?}", e); } else if !cov.is_empty() { - let cx = kfs::boot_fs_context().lock(); + let cx = kfs::kernel_fs_context().lock(); let root = cx.root_dir().clone(); let fs_ops = kfs::FsOperations::new(root); diff --git a/fs/kfs/src/highlevel/fs.rs b/fs/kfs/src/highlevel/fs.rs index d0bf6305..938f5da6 100644 --- a/fs/kfs/src/highlevel/fs.rs +++ b/fs/kfs/src/highlevel/fs.rs @@ -30,19 +30,19 @@ use crate::fs_operations::FsOperations as FsContextImpl; /// Global root filesystem context initializer. pub static ROOT_FS_CONTEXT: Once = Once::new(); -/// Boot-time filesystem context shared before any user process exists. -pub static BOOT_FS_CONTEXT: Once>> = Once::new(); +/// Kernel-default filesystem context shared by boot and kernel-task paths. +pub static KERNEL_FS_CONTEXT: Once>> = Once::new(); -/// Returns the boot-time filesystem context. -pub fn boot_fs_context() -> &'static Arc> { - BOOT_FS_CONTEXT +/// Returns the kernel-default filesystem context. +pub fn kernel_fs_context() -> &'static Arc> { + KERNEL_FS_CONTEXT .get() - .expect("Boot FS context not initialized") + .expect("kernel FS context not initialized") } -/// Creates a new process-owned filesystem context from the boot defaults. +/// Creates a new process-owned filesystem context from the kernel defaults. pub fn new_process_fs_context() -> Arc> { - Arc::new(Mutex::new(boot_fs_context().lock().clone())) + Arc::new(Mutex::new(kernel_fs_context().lock().clone())) } /// Directory entry returned by `ReadDir`. diff --git a/fs/kfs/src/highlevel/mod.rs b/fs/kfs/src/highlevel/mod.rs index ade5f7b5..5a5a6c7d 100644 --- a/fs/kfs/src/highlevel/mod.rs +++ b/fs/kfs/src/highlevel/mod.rs @@ -9,6 +9,6 @@ mod fs; pub use file::*; // Re-export the wrapper FsContext for backward compatibility pub use fs::{ - BOOT_FS_CONTEXT, FsContext, ROOT_FS_CONTEXT, ReadDir, ReadDirEntry, boot_fs_context, + FsContext, KERNEL_FS_CONTEXT, ROOT_FS_CONTEXT, ReadDir, ReadDirEntry, kernel_fs_context, new_process_fs_context, }; diff --git a/fs/kfs/src/lib.rs b/fs/kfs/src/lib.rs index 61c1fc52..1d93d1bb 100644 --- a/fs/kfs/src/lib.rs +++ b/fs/kfs/src/lib.rs @@ -73,7 +73,7 @@ pub fn init_filesystems(mut block_devs: DeviceContainer) { let mp = fs_ng_vfs::Mountpoint::new_root(&fs); let root_context = FsContext::new(mp.root_location()); ROOT_FS_CONTEXT.call_once(|| root_context.clone()); - BOOT_FS_CONTEXT.call_once(|| Arc::new(Mutex::new(root_context))); + KERNEL_FS_CONTEXT.call_once(|| Arc::new(Mutex::new(root_context))); } #[cfg(feature = "fs9p")] @@ -87,7 +87,7 @@ pub fn mount_9pfilesystems(mut virtio_9p_devs: DeviceContainer, let fs = fs::new_9p_filesystem(dev_9p).expect("Failed to initialize filesystem"); info!(" filesystem type: {:?}", fs.name()); - let mut fs_ctx = boot_fs_context().lock(); + let mut fs_ctx = kernel_fs_context().lock(); ensure_mount_path(&mut fs_ctx, mount_path).expect("Failed to prepare 9P mount path"); fs_ctx .resolve(mount_path) diff --git a/net/knet/src/unix.rs b/net/knet/src/unix.rs index 553c99f6..087b4f38 100644 --- a/net/knet/src/unix.rs +++ b/net/knet/src/unix.rs @@ -19,7 +19,6 @@ use kio::{IoBuf, Read, Write}; use kpoll::{IoEvents, Pollable}; use ksync::Mutex; use ktask::future::{block_on, interruptible}; -use kthread::current_process_state; use lazy_static::lazy_static; pub use self::{dgram::DgramTransport, stream::StreamTransport}; @@ -100,8 +99,7 @@ pub(crate) fn lookup_bind_entry( } } UnixAddr::Path(path) => { - let loc = current_process_state() - .fs_context() + let loc = kthread::current_fs_context() .lock() .resolve(path.as_ref())?; if loc.metadata()?.node_type != NodeType::Socket { @@ -130,7 +128,7 @@ fn lookup_or_create_bind_entry( .write(true) .create(true) .node_type(NodeType::Socket) - .open(¤t_process_state().fs_context().lock(), path.as_ref())? + .open(&kthread::current_fs_context().lock(), path.as_ref())? .into_location(); if loc.metadata()?.node_type != NodeType::Socket { return Err(KError::NotASocket); diff --git a/posix/fs/src/io.rs b/posix/fs/src/io.rs index c4081bb1..d4d6758c 100644 --- a/posix/fs/src/io.rs +++ b/posix/fs/src/io.rs @@ -167,7 +167,7 @@ pub fn sys_truncate(path: UserConstPtr, length: __kernel_off_t) -> KResu } let file = OpenOptions::new() .write(true) - .open(&kthread::current_process_state().fs_context().lock(), path)? + .open(&kthread::current_process_fs_context().lock(), path)? .into_file()?; file.access(FileFlags::WRITE)?.set_len(length as _)?; Ok(0) diff --git a/posix/fs/src/open.rs b/posix/fs/src/open.rs index 3a798d27..4f96e0c7 100644 --- a/posix/fs/src/open.rs +++ b/posix/fs/src/open.rs @@ -97,7 +97,9 @@ fn add_to_fd(result: OpenResult, flags: u32) -> KResult { } else { panic!("unknown terminal type") }; - let loc = current_process_state().fs_context().lock().resolve(&path)?; + let loc = kthread::current_process_fs_context() + .lock() + .resolve(&path)?; file = kfs::File::new(FileBackend::Direct(loc), file.flags()); } } diff --git a/posix/mm/src/memfd.rs b/posix/mm/src/memfd.rs index 6c27af78..92acada8 100644 --- a/posix/mm/src/memfd.rs +++ b/posix/mm/src/memfd.rs @@ -10,7 +10,7 @@ use core::ffi::c_char; use kerrno::{KError, KResult}; use kfs::OpenOptions; use kservices::file::File; -use kthread::current_process_state; +use kthread::{current_process_fs_context, current_process_state}; use linux_raw_sys::general::{MFD_CLOEXEC, O_RDWR}; use posix_types::UserConstPtr; @@ -21,7 +21,7 @@ pub fn sys_memfd_create(_name: UserConstPtr, flags: u32) -> KResult String { current().name() } +/// Returns the current filesystem context for shared current-path helpers. +/// +/// Shared subsystems should use this helper when they only need a usable +/// path-resolution context. User threads get their process-owned context, +/// while kernel tasks fall back to the kernel-default context. +pub fn current_fs_context() -> Arc> { + current() + .try_as_thread() + .map(|thread| thread.proc_state.fs_context().clone()) + .unwrap_or_else(|| kernel_fs_context().clone()) +} + +/// Returns the current process-owned filesystem context. +/// +/// Use this helper only for process-only paths such as syscalls or POSIX +/// logic that require a current user thread. Kernel-task callers should use +/// `current_fs_context` instead. +pub fn current_process_fs_context() -> Arc> { + current_process_state().fs_context().clone() +} + /// Executes a closure with the current user thread. pub fn with_current_thread(f: impl FnOnce(&Thread) -> R) -> R { let thread = current_thread(); @@ -35,6 +58,8 @@ pub fn with_current_thread(f: impl FnOnce(&Thread) -> R) -> R { } /// Returns the current process state. +/// +/// This helper requires the current task to be a user thread. pub fn current_process_state() -> Arc { with_current_thread(|thread| thread.process_state().clone()) } diff --git a/process/kthread/src/thread/mod.rs b/process/kthread/src/thread/mod.rs index 67965452..2d4cf01c 100644 --- a/process/kthread/src/thread/mod.rs +++ b/process/kthread/src/thread/mod.rs @@ -8,5 +8,8 @@ mod task_ext; pub use core::{AssumeSync, CurrentThread, Thread}; -pub use current::{current_process_state, current_task_name, current_thread, with_current_thread}; +pub use current::{ + current_fs_context, current_process_fs_context, current_process_state, current_task_name, + current_thread, with_current_thread, +}; pub use task_ext::AsThread; diff --git a/tee/tee_kernel/src/file.rs b/tee/tee_kernel/src/file.rs index fb284cf9..f7be062f 100644 --- a/tee/tee_kernel/src/file.rs +++ b/tee/tee_kernel/src/file.rs @@ -7,7 +7,6 @@ use core::ffi::c_int; use fs_ng_vfs::{Location, Metadata}; use kerrno::{KError, KResult}; use kfs::FsContext; -use kthread::current_process_state; use linux_raw_sys::general::{AT_FDCWD, AT_SYMLINK_NOFOLLOW}; pub fn with_fs(dirfd: c_int, f: impl FnOnce(&mut FsContext) -> KResult) -> KResult { @@ -15,8 +14,10 @@ pub fn with_fs(dirfd: c_int, f: impl FnOnce(&mut FsContext) -> KResult) -> return Err(KError::InvalidInput); } - let proc_state = current_process_state(); - let mut fs = proc_state.fs_context().lock(); + // TEE file helpers are callable from both process and kernel-task paths, + // so they must use the shared current-path filesystem view. + let fs_context = kthread::current_fs_context(); + let mut fs = fs_context.lock(); f(&mut fs) } diff --git a/tee/tee_task_iface/src/tasign.rs b/tee/tee_task_iface/src/tasign.rs index 6a9ecbb9..94224eb4 100644 --- a/tee/tee_task_iface/src/tasign.rs +++ b/tee/tee_task_iface/src/tasign.rs @@ -9,7 +9,7 @@ use core::convert::AsRef; use hashbrown::HashMap; use kerrno::{KError, KResult}; -use kfs::{CachedFile, boot_fs_context}; +use kfs::{CachedFile, kernel_fs_context}; use log::{error, info}; use spin::{Lazy, Mutex}; use tee_raw_sys::ta_head; @@ -137,7 +137,7 @@ pub fn verify_ta_elf_on_load_and_cache_ta_head(cache: &CachedFile) -> KResult<() /// raw user-provided exec path so lookup stays independent of per-process cwd /// or chroot state. pub fn get_ta_head_cached(path: &str) -> KResult>> { - let loc = boot_fs_context().lock().resolve(path)?; + let loc = kernel_fs_context().lock().resolve(path)?; let abs = loc.absolute_path()?; let key: String = String::from(AsRef::::as_ref(&abs)); -- Gitee From 21e695128f962af42de81fc5b69bd15b06c65226 Mon Sep 17 00:00:00 2001 From: Weikang Guo Date: Sat, 9 May 2026 09:47:38 +0800 Subject: [PATCH 17/21] fix clippy Signed-off-by: Weikang Guo --- process/kthread/src/process_state.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/process/kthread/src/process_state.rs b/process/kthread/src/process_state.rs index 4dbdd75f..907e6458 100644 --- a/process/kthread/src/process_state.rs +++ b/process/kthread/src/process_state.rs @@ -12,6 +12,7 @@ use core::any::Any; use hashbrown::HashMap; use kcred::Credentials; +#[cfg(feature = "tee")] use kerrno::{KError, KResult}; use kfs::FsContext; use kfutex::{FutexKey, FutexTable}; -- Gitee From d948eddb1ccf0ec05954ce78dd1332d5626f1e22 Mon Sep 17 00:00:00 2001 From: Weikang Guo Date: Sat, 9 May 2026 10:38:54 +0800 Subject: [PATCH 18/21] remove core config Signed-off-by: Weikang Guo --- Kconfig | 6 +++++ core/kcore/src/config/aarch64.rs | 8 ------ core/kcore/src/config/loongarch64.rs | 8 ------ core/kcore/src/config/mod.rs | 27 ------------------- core/kcore/src/config/riscv64.rs | 8 ------ core/kcore/src/config/x86_64.rs | 8 ------ core/kcore/src/lib.rs | 1 - core/kservices/Cargo.toml | 1 + core/kservices/src/task.rs | 3 ++- platforms/aarch64-crosvm-virt/defconfig | 1 + platforms/aarch64-qemu-virt/defconfig | 3 ++- platforms/aarch64-qemu-virt/virtcca_defconfig | 1 + platforms/loongarch64-qemu-virt/defconfig | 1 + platforms/riscv64-qemu-virt/defconfig | 1 + platforms/x86_64-qemu-virt/csv_defconfig | 3 ++- platforms/x86_64-qemu-virt/defconfig | 1 + 16 files changed, 18 insertions(+), 63 deletions(-) delete mode 100644 core/kcore/src/config/aarch64.rs delete mode 100644 core/kcore/src/config/loongarch64.rs delete mode 100644 core/kcore/src/config/mod.rs delete mode 100644 core/kcore/src/config/riscv64.rs delete mode 100644 core/kcore/src/config/x86_64.rs diff --git a/Kconfig b/Kconfig index 4460f88a..9642943c 100644 --- a/Kconfig +++ b/Kconfig @@ -142,6 +142,12 @@ config TASK_STACK_SIZE help Specify the stack size for each task in bytes. +config KERNEL_STACK_SIZE + hex "Kernel Thread Stack Size (bytes)" + default 0x40000 + help + Specify the stack size for kernel worker threads in bytes. + config BOOT_STACK_SIZE hex "Boot Stack Size (bytes)" default 0x40000 diff --git a/core/kcore/src/config/aarch64.rs b/core/kcore/src/config/aarch64.rs deleted file mode 100644 index 9a1cb465..00000000 --- a/core/kcore/src/config/aarch64.rs +++ /dev/null @@ -1,8 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright 2025 KylinSoft Co., Ltd. -// See LICENSES for license details. - -//! Architecture-specific configuration for aarch64. - -/// The size of the kernel stack. -pub const KERNEL_STACK_SIZE: usize = 0x4_0000; diff --git a/core/kcore/src/config/loongarch64.rs b/core/kcore/src/config/loongarch64.rs deleted file mode 100644 index 7b8dc3b5..00000000 --- a/core/kcore/src/config/loongarch64.rs +++ /dev/null @@ -1,8 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright 2025 KylinSoft Co., Ltd. -// See LICENSES for license details. - -//! Architecture-specific configuration for loongarch64. - -/// The size of the kernel stack. -pub const KERNEL_STACK_SIZE: usize = 0x4_0000; diff --git a/core/kcore/src/config/mod.rs b/core/kcore/src/config/mod.rs deleted file mode 100644 index 461f103f..00000000 --- a/core/kcore/src/config/mod.rs +++ /dev/null @@ -1,27 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright 2025 KylinSoft Co., Ltd. -// See LICENSES for license details. - -//! Architecture-specific configurations. - -cfg_if::cfg_if! { - if #[cfg(target_arch = "riscv64")] { - #[rustfmt::skip] - mod riscv64; - pub use riscv64::*; - } else if #[cfg(target_arch = "loongarch64")] { - #[rustfmt::skip] - mod loongarch64; - pub use loongarch64::*; - } else if #[cfg(target_arch = "x86_64")] { - #[rustfmt::skip] - mod x86_64; - pub use x86_64::*; - } else if #[cfg(target_arch = "aarch64")] { - #[rustfmt::skip] - mod aarch64; - pub use aarch64::*; - } else { - compile_error!("Unsupported architecture"); - } -} diff --git a/core/kcore/src/config/riscv64.rs b/core/kcore/src/config/riscv64.rs deleted file mode 100644 index 68b75924..00000000 --- a/core/kcore/src/config/riscv64.rs +++ /dev/null @@ -1,8 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright 2025 KylinSoft Co., Ltd. -// See LICENSES for license details. - -//! Architecture-specific configuration for riscv64. - -/// The size of the kernel stack. -pub const KERNEL_STACK_SIZE: usize = 0x4_0000; diff --git a/core/kcore/src/config/x86_64.rs b/core/kcore/src/config/x86_64.rs deleted file mode 100644 index 484914de..00000000 --- a/core/kcore/src/config/x86_64.rs +++ /dev/null @@ -1,8 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright 2025 KylinSoft Co., Ltd. -// See LICENSES for license details. - -//! Architecture-specific configuration for x86_64. - -/// The size of the kernel stack. -pub const KERNEL_STACK_SIZE: usize = 0x4_0000; diff --git a/core/kcore/src/lib.rs b/core/kcore/src/lib.rs index 32cdb544..705d87a4 100644 --- a/core/kcore/src/lib.rs +++ b/core/kcore/src/lib.rs @@ -15,7 +15,6 @@ extern crate alloc; #[macro_use] extern crate klogger; -pub mod config; mod lrucache; pub mod mm; pub mod vfs; diff --git a/core/kservices/Cargo.toml b/core/kservices/Cargo.toml index 1978d178..68b92e97 100644 --- a/core/kservices/Cargo.toml +++ b/core/kservices/Cargo.toml @@ -38,6 +38,7 @@ kfd.workspace = true kdriver.workspace = true ktypes.workspace = true kerrno.workspace = true +kbuild_config.workspace = true fs-ng-vfs.workspace = true kfs.workspace = true khal.workspace = true diff --git a/core/kservices/src/task.rs b/core/kservices/src/task.rs index a830841d..f4bc31ae 100644 --- a/core/kservices/src/task.rs +++ b/core/kservices/src/task.rs @@ -7,6 +7,7 @@ use core::{ffi::c_long, sync::atomic::Ordering}; use bytemuck::AnyBitPattern; +use kbuild_config::KERNEL_STACK_SIZE; use kerrno::{KError, KResult}; use khal::uspace::{ExceptionKind, ReturnReason, UserContext}; use kprocess::Pid; @@ -97,7 +98,7 @@ pub fn new_user_task( } }, name.into(), - kcore::config::KERNEL_STACK_SIZE, + KERNEL_STACK_SIZE, ) } diff --git a/platforms/aarch64-crosvm-virt/defconfig b/platforms/aarch64-crosvm-virt/defconfig index 582c8097..cba06d9d 100644 --- a/platforms/aarch64-crosvm-virt/defconfig +++ b/platforms/aarch64-crosvm-virt/defconfig @@ -57,4 +57,5 @@ PLATFORM_AARCH64_CROSVM_VIRT=y PMU_IRQ=23 PSCI_METHOD="hvc" TASK_STACK_SIZE=0x40000 +KERNEL_STACK_SIZE=0x40000 TICKS_PER_SECOND=100 diff --git a/platforms/aarch64-qemu-virt/defconfig b/platforms/aarch64-qemu-virt/defconfig index 5e7666e9..8b0fb7f4 100644 --- a/platforms/aarch64-qemu-virt/defconfig +++ b/platforms/aarch64-qemu-virt/defconfig @@ -58,5 +58,6 @@ PMU_IRQ=23 PSCI_METHOD="hvc" RTC_PADDR=0x09010000 TASK_STACK_SIZE=0x40000 +KERNEL_STACK_SIZE=0x40000 TICKS_PER_SECOND=100 -# KFEAT_VIRTCCA_HUK_KEY is not set \ No newline at end of file +# KFEAT_VIRTCCA_HUK_KEY is not set diff --git a/platforms/aarch64-qemu-virt/virtcca_defconfig b/platforms/aarch64-qemu-virt/virtcca_defconfig index 7e9e3762..d68ac222 100644 --- a/platforms/aarch64-qemu-virt/virtcca_defconfig +++ b/platforms/aarch64-qemu-virt/virtcca_defconfig @@ -61,4 +61,5 @@ PMU_IRQ=23 PSCI_METHOD="smc" RTC_PADDR=0x09010000 TASK_STACK_SIZE=0x40000 +KERNEL_STACK_SIZE=0x40000 TICKS_PER_SECOND=100 diff --git a/platforms/loongarch64-qemu-virt/defconfig b/platforms/loongarch64-qemu-virt/defconfig index b4331fbc..6072344c 100644 --- a/platforms/loongarch64-qemu-virt/defconfig +++ b/platforms/loongarch64-qemu-virt/defconfig @@ -55,4 +55,5 @@ PLATFORM_LOONGARCH64_QEMU_VIRT=y # PLATFORM_X86_64_QEMU_VIRT is not set RTC_PADDR=0x100d0100 TASK_STACK_SIZE=0x40000 +KERNEL_STACK_SIZE=0x40000 TICKS_PER_SECOND=100 diff --git a/platforms/riscv64-qemu-virt/defconfig b/platforms/riscv64-qemu-virt/defconfig index 5105bce8..c4eb6e22 100644 --- a/platforms/riscv64-qemu-virt/defconfig +++ b/platforms/riscv64-qemu-virt/defconfig @@ -55,4 +55,5 @@ PLATFORM_RISCV64_QEMU_VIRT=y # PLATFORM_X86_64_QEMU_VIRT is not set RTC_PADDR=0x00101000 TASK_STACK_SIZE=0x40000 +KERNEL_STACK_SIZE=0x40000 TICKS_PER_SECOND=100 diff --git a/platforms/x86_64-qemu-virt/csv_defconfig b/platforms/x86_64-qemu-virt/csv_defconfig index 6de81ed2..aa9add35 100644 --- a/platforms/x86_64-qemu-virt/csv_defconfig +++ b/platforms/x86_64-qemu-virt/csv_defconfig @@ -57,5 +57,6 @@ PLATFORM="x86_64-qemu-virt" PLATFORM_X86_64_QEMU_VIRT=y SEV_CBIT_POS=47 TASK_STACK_SIZE=0x40000 +KERNEL_STACK_SIZE=0x40000 TICKS_PER_SECOND=100 -TIMER_FREQUENCY_HZ=4000000000 \ No newline at end of file +TIMER_FREQUENCY_HZ=4000000000 diff --git a/platforms/x86_64-qemu-virt/defconfig b/platforms/x86_64-qemu-virt/defconfig index 8be62ead..bc973b8c 100644 --- a/platforms/x86_64-qemu-virt/defconfig +++ b/platforms/x86_64-qemu-virt/defconfig @@ -55,5 +55,6 @@ PLATFORM="x86_64-qemu-virt" PLATFORM_X86_64_QEMU_VIRT=y SEV_CBIT_POS=0 TASK_STACK_SIZE=0x40000 +KERNEL_STACK_SIZE=0x40000 TICKS_PER_SECOND=100 TIMER_FREQUENCY_HZ=4000000000 -- Gitee From b9a46ee4929b2c8611ab2b14cc538e782f17dfdd Mon Sep 17 00:00:00 2001 From: Weikang Guo Date: Sat, 9 May 2026 10:46:02 +0800 Subject: [PATCH 19/21] Refactor kaddr layout selection Signed-off-by: Weikang Guo --- mm/kaddr_layout/src/lib.rs | 148 ++++++++++++++----------------------- 1 file changed, 54 insertions(+), 94 deletions(-) diff --git a/mm/kaddr_layout/src/lib.rs b/mm/kaddr_layout/src/lib.rs index ff5ab2ed..42b9fd0e 100644 --- a/mm/kaddr_layout/src/lib.rs +++ b/mm/kaddr_layout/src/lib.rs @@ -40,65 +40,48 @@ pub struct UserLayoutConsts { pub user_stack_size: usize, } -pub fn for_arch(arch: &str) -> LayoutConsts { +fn arch_layouts(arch: &str) -> (LayoutConsts, UserLayoutConsts) { match arch { - "aarch64" => aarch64::LAYOUT, - "riscv64" => riscv64::LAYOUT, - "x86_64" => x86_64::LAYOUT, - "riscv32" => fallback::LAYOUT, - "loongarch64" => loongarch64::LAYOUT, - _ => fallback::LAYOUT, + "aarch64" => (aarch64::LAYOUT, aarch64::USER_LAYOUT), + "riscv64" => (riscv64::LAYOUT, riscv64::USER_LAYOUT), + "x86_64" => (x86_64::LAYOUT, x86_64::USER_LAYOUT), + "riscv32" => (fallback::LAYOUT, fallback::USER_LAYOUT), + "loongarch64" => (loongarch64::LAYOUT, loongarch64::USER_LAYOUT), + _ => (fallback::LAYOUT, fallback::USER_LAYOUT), } } -pub fn user_layout_for_arch(arch: &str) -> UserLayoutConsts { - match arch { - "aarch64" => aarch64::USER_LAYOUT, - "riscv64" => riscv64::USER_LAYOUT, - "x86_64" => x86_64::USER_LAYOUT, - "riscv32" => fallback::USER_LAYOUT, - "loongarch64" => loongarch64::USER_LAYOUT, - _ => fallback::USER_LAYOUT, +cfg_select! { + target_arch = "aarch64" => { + use self::aarch64 as current_arch_layout; + } + target_arch = "x86_64" => { + use self::x86_64 as current_arch_layout; + } + target_arch = "riscv64" => { + use self::riscv64 as current_arch_layout; + } + target_arch = "loongarch64" => { + use self::loongarch64 as current_arch_layout; } + target_arch = "riscv32" => { + use self::fallback as current_arch_layout; + } + _ => { + use self::fallback as current_arch_layout; + } +} + +pub fn for_arch(arch: &str) -> LayoutConsts { + arch_layouts(arch).0 +} + +pub fn user_layout_for_arch(arch: &str) -> UserLayoutConsts { + arch_layouts(arch).1 } -#[cfg(target_arch = "aarch64")] -const CURRENT_LAYOUT: LayoutConsts = aarch64::LAYOUT; -#[cfg(target_arch = "x86_64")] -const CURRENT_LAYOUT: LayoutConsts = x86_64::LAYOUT; -#[cfg(target_arch = "riscv32")] -const CURRENT_LAYOUT: LayoutConsts = fallback::LAYOUT; -#[cfg(target_arch = "riscv64")] -const CURRENT_LAYOUT: LayoutConsts = riscv64::LAYOUT; -#[cfg(target_arch = "loongarch64")] -const CURRENT_LAYOUT: LayoutConsts = loongarch64::LAYOUT; -#[cfg(not(any( - target_arch = "aarch64", - target_arch = "x86_64", - target_arch = "riscv32", - target_arch = "riscv64", - target_arch = "loongarch64" -)))] -const CURRENT_LAYOUT: LayoutConsts = fallback::LAYOUT; - -#[cfg(target_arch = "aarch64")] -const CURRENT_USER_LAYOUT: UserLayoutConsts = aarch64::USER_LAYOUT; -#[cfg(target_arch = "x86_64")] -const CURRENT_USER_LAYOUT: UserLayoutConsts = x86_64::USER_LAYOUT; -#[cfg(target_arch = "riscv32")] -const CURRENT_USER_LAYOUT: UserLayoutConsts = fallback::USER_LAYOUT; -#[cfg(target_arch = "riscv64")] -const CURRENT_USER_LAYOUT: UserLayoutConsts = riscv64::USER_LAYOUT; -#[cfg(target_arch = "loongarch64")] -const CURRENT_USER_LAYOUT: UserLayoutConsts = loongarch64::USER_LAYOUT; -#[cfg(not(any( - target_arch = "aarch64", - target_arch = "x86_64", - target_arch = "riscv32", - target_arch = "riscv64", - target_arch = "loongarch64" -)))] -const CURRENT_USER_LAYOUT: UserLayoutConsts = fallback::USER_LAYOUT; +const CURRENT_LAYOUT: LayoutConsts = current_arch_layout::LAYOUT; +const CURRENT_USER_LAYOUT: UserLayoutConsts = current_arch_layout::USER_LAYOUT; pub const PG_VA_BITS: usize = CURRENT_LAYOUT.pg_va_bits; pub const KERNEL_ASPACE_BASE: usize = CURRENT_LAYOUT.kernel_aspace_base; @@ -180,59 +163,36 @@ pub fn p2v(pa: usize) -> usize { pa + PAGE_OFFSET } -#[cfg(any( - target_arch = "aarch64", - target_arch = "loongarch64", - target_arch = "x86_64", - target_arch = "riscv64" -))] #[inline] const fn in_window(va: usize, start: usize, size: usize) -> bool { va >= start && (va - start) < size } -/// Convert a virtual address to its physical address. -/// -/// AArch64/x86_64 keep the linked kernel image in a dedicated higher-half -/// window distinct from the linear map, so kernel-image VAs must subtract the -/// runtime `kimage_voffset()`. -#[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))] #[inline] -pub fn v2p(va: usize) -> usize { - if in_window(va, KIMAGE_VADDR, KIMAGE_VSIZE) { +pub(crate) fn v2p_with_kimage_window(va: usize, layout: LayoutConsts) -> usize { + if in_window(va, layout.kimage_vaddr, layout.kimage_vsize) { va - kimage_voffset() - } else if in_window(va, LINEAR_MAP_VADDR, LINEAR_MAP_VSIZE) { - va - PAGE_OFFSET + } else if in_window(va, layout.linear_map_vaddr, layout.linear_map_vsize) { + va - layout.page_offset } else { panic!("v2p only supports linear-map or kernel-image addresses: {va:#x}"); } } -/// Convert a virtual address to its physical address. -/// -/// RISC-V currently uses a dedicated kernel-image alias window too, so it -/// shares the same split logic as AArch64/x86_64. -#[cfg(any(target_arch = "riscv64", target_arch = "loongarch64"))] -#[inline] -pub fn v2p(va: usize) -> usize { - if in_window(va, KIMAGE_VADDR, KIMAGE_VSIZE) { - va - kimage_voffset() - } else if in_window(va, LINEAR_MAP_VADDR, LINEAR_MAP_VSIZE) { - va - PAGE_OFFSET - } else { - panic!("v2p only supports linear-map or kernel-image addresses: {va:#x}"); +cfg_select! { + any( + target_arch = "aarch64", + target_arch = "loongarch64", + target_arch = "x86_64", + target_arch = "riscv64" + ) => { + /// Convert a virtual address to its physical address. + #[inline] + pub fn v2p(va: usize) -> usize { + v2p_with_kimage_window(va, CURRENT_LAYOUT) + } + } + _ => { + compile_error!("`v2p` is only supported on the current 64-bit kernel architectures"); } -} - -/// Fallback architectures still translate kernel VAs through the linear-map -/// offset only. -#[cfg(not(any( - target_arch = "aarch64", - target_arch = "loongarch64", - target_arch = "x86_64", - target_arch = "riscv64" -)))] -#[inline] -pub fn v2p(va: usize) -> usize { - va - PAGE_OFFSET } -- Gitee From da2a1c38ec5530fabdc40a4f0a88e26296164b36 Mon Sep 17 00:00:00 2001 From: Weikang Guo Date: Sat, 9 May 2026 10:57:04 +0800 Subject: [PATCH 20/21] remove log_prefix define Signed-off-by: Weikang Guo --- .../asterinas-coding-guidelines/SKILL.md | 41 ++++--------------- posix/process/src/lib.rs | 6 --- posix/signal/src/lib.rs | 6 --- posix/sync/src/lib.rs | 6 --- process/kfd/src/lib.rs | 6 --- process/kfutex/src/lib.rs | 6 --- process/kresources/src/lib.rs | 6 --- process/krlimit/src/lib.rs | 6 --- process/kthread/src/lib.rs | 6 --- process/ktimer/src/lib.rs | 6 --- 10 files changed, 8 insertions(+), 87 deletions(-) diff --git a/.claude/skills/asterinas-coding-guidelines/SKILL.md b/.claude/skills/asterinas-coding-guidelines/SKILL.md index 379aa77b..5421be1c 100644 --- a/.claude/skills/asterinas-coding-guidelines/SKILL.md +++ b/.claude/skills/asterinas-coding-guidelines/SKILL.md @@ -1234,43 +1234,18 @@ Use `crit!` or `emerg!` only for failures immediately before a halt or abort. A log statement that fires on every syscall or every timer tick must use `debug!`. -#### Define a log prefix for each crate (`log-prefix`) {#log-prefix} +#### Keep log prefixes consistent with the active logger (`log-prefix`) {#log-prefix} -Every OSTD-based crate must define a `__log_prefix` macro at its crate root (in `lib.rs`), -before any `mod` declarations. -This labels all log messages from the crate: +Only define a `__log_prefix` macro +when the active logger consumes it +and it adds useful signal +beyond the default module, file, or line metadata. -```rust -// Set this crate's log prefix for `ostd::log`. -macro_rules! __log_prefix { - () => { - "virtio: " - }; -} -``` - -Convention: use the lowercase crate name (without `aster_` prefix), followed by `: `. -For example: `"virtio: "`, `"pci: "`, `"uart: "`. - -Subsystem modules within a crate can override the prefix -by defining their own `__log_prefix` at the top of `mod.rs`: - -```rust -// Set this module's log prefix for `ostd::log`. -macro_rules! __log_prefix { - () => { - "net: " - }; -} -``` - -Child modules inherit the override automatically. - -Do not put `#[rustfmt::skip]` or any other attribute on `__log_prefix` definitions — -it causes a compiler ambiguity error (E0659). +If the current logger already prints +clear source-location context, +prefer not to add a redundant prefix macro. Do not use manual bracket prefixes like `[IOMMU]` or `[Virtio]:`. -The `__log_prefix` mechanism replaces them. ### Memory and Resource Management diff --git a/posix/process/src/lib.rs b/posix/process/src/lib.rs index 082a6358..c6e3e438 100644 --- a/posix/process/src/lib.rs +++ b/posix/process/src/lib.rs @@ -11,12 +11,6 @@ #![no_std] -macro_rules! __log_prefix { - () => { - "process: " - }; -} - use kerrno::{KError, KResult}; use khal::time::TimeValue; use kprocess::{Pid, Process}; diff --git a/posix/signal/src/lib.rs b/posix/signal/src/lib.rs index 0dae058a..e5be3936 100644 --- a/posix/signal/src/lib.rs +++ b/posix/signal/src/lib.rs @@ -14,12 +14,6 @@ #[macro_use] extern crate klogger; -macro_rules! __log_prefix { - () => { - "signal: " - }; -} - use core::{future::poll_fn, task::Poll}; use kerrno::{KError, KResult, LinuxError}; diff --git a/posix/sync/src/lib.rs b/posix/sync/src/lib.rs index e411f298..4b8e1fe6 100644 --- a/posix/sync/src/lib.rs +++ b/posix/sync/src/lib.rs @@ -9,12 +9,6 @@ #[macro_use] extern crate klogger; -macro_rules! __log_prefix { - () => { - "sync: " - }; -} - use core::{mem::size_of, sync::atomic::Ordering}; use kerrno::{KError, KResult, LinuxError}; diff --git a/process/kfd/src/lib.rs b/process/kfd/src/lib.rs index ad1781c3..092ca281 100644 --- a/process/kfd/src/lib.rs +++ b/process/kfd/src/lib.rs @@ -8,12 +8,6 @@ extern crate alloc; -macro_rules! __log_prefix { - () => { - "kfd: " - }; -} - mod fd_table; mod file_descriptor; mod file_like; diff --git a/process/kfutex/src/lib.rs b/process/kfutex/src/lib.rs index c560820c..c0d8482c 100644 --- a/process/kfutex/src/lib.rs +++ b/process/kfutex/src/lib.rs @@ -6,12 +6,6 @@ #![no_std] -macro_rules! __log_prefix { - () => { - "kfutex: " - }; -} - extern crate alloc; use alloc::{ diff --git a/process/kresources/src/lib.rs b/process/kresources/src/lib.rs index dcad169e..8d244448 100644 --- a/process/kresources/src/lib.rs +++ b/process/kresources/src/lib.rs @@ -6,12 +6,6 @@ #![no_std] -macro_rules! __log_prefix { - () => { - "kresources: " - }; -} - extern crate alloc; use alloc::sync::Arc; diff --git a/process/krlimit/src/lib.rs b/process/krlimit/src/lib.rs index f1a1ef17..969b6d9c 100644 --- a/process/krlimit/src/lib.rs +++ b/process/krlimit/src/lib.rs @@ -6,12 +6,6 @@ #![no_std] -macro_rules! __log_prefix { - () => { - "krlimit: " - }; -} - use core::ops::{Index, IndexMut}; use linux_raw_sys::general::{RLIM_NLIMITS, RLIMIT_NOFILE, RLIMIT_STACK}; diff --git a/process/kthread/src/lib.rs b/process/kthread/src/lib.rs index 119c9d94..3578315c 100644 --- a/process/kthread/src/lib.rs +++ b/process/kthread/src/lib.rs @@ -10,12 +10,6 @@ #![no_std] -macro_rules! __log_prefix { - () => { - "kthread: " - }; -} - extern crate alloc; use alloc::sync::Arc; diff --git a/process/ktimer/src/lib.rs b/process/ktimer/src/lib.rs index 5e24a558..ad52d77e 100644 --- a/process/ktimer/src/lib.rs +++ b/process/ktimer/src/lib.rs @@ -6,12 +6,6 @@ #![no_std] -macro_rules! __log_prefix { - () => { - "ktimer: " - }; -} - extern crate alloc; use alloc::{borrow::ToOwned, collections::binary_heap::BinaryHeap, sync::Arc}; -- Gitee From 52aacf10fd030daa6516553a6844f7504b580261 Mon Sep 17 00:00:00 2001 From: Weikang Guo Date: Sat, 9 May 2026 11:20:21 +0800 Subject: [PATCH 21/21] add rlimit default init Signed-off-by: Weikang Guo --- process/krlimit/src/lib.rs | 47 +++++++++++++++++++++++++++++++++----- 1 file changed, 41 insertions(+), 6 deletions(-) diff --git a/process/krlimit/src/lib.rs b/process/krlimit/src/lib.rs index 969b6d9c..5d97181d 100644 --- a/process/krlimit/src/lib.rs +++ b/process/krlimit/src/lib.rs @@ -8,13 +8,20 @@ use core::ops::{Index, IndexMut}; -use linux_raw_sys::general::{RLIM_NLIMITS, RLIMIT_NOFILE, RLIMIT_STACK}; +use linux_raw_sys::general::{ + RLIM_NLIMITS, RLIMIT_CORE, RLIMIT_MEMLOCK, RLIMIT_MSGQUEUE, RLIMIT_NICE, RLIMIT_NOFILE, + RLIMIT_NPROC, RLIMIT_RTPRIO, RLIMIT_SIGPENDING, RLIMIT_STACK, +}; -/// The maximum number of open files. +const RLIM_INFINITY: u64 = u64::MAX; +const MLOCK_LIMIT_BYTES: u64 = 8 * 1024 * 1024; +const MSGQUEUE_LIMIT_BYTES: u64 = 819_200; + +/// The maximum number of open files supported by the current fd table. pub const FILE_LIMIT: usize = 1024; /// The limit for a specific resource. -#[derive(Default)] +#[derive(Clone, Copy, Debug, Eq, PartialEq)] pub struct Rlimit { /// The current limit for the resource (soft). pub current: u64, @@ -23,8 +30,11 @@ pub struct Rlimit { } impl Rlimit { + /// The unlimited soft/hard limit pair. + pub const INFINITY: Self = Self::new(RLIM_INFINITY, RLIM_INFINITY); + /// Creates a new `Rlimit` with the specified soft and hard limits. - pub fn new(soft: u64, hard: u64) -> Self { + pub const fn new(soft: u64, hard: u64) -> Self { Self { current: soft, max: hard, @@ -45,11 +55,26 @@ impl From for Rlimit { pub struct Rlimits([Rlimit; RLIM_NLIMITS as usize]); impl Rlimits { - /// Creates a new limit table with the default stack and file-descriptor caps. + /// Creates a new limit table with Linux-like defaults. pub fn new(user_stack_size: usize) -> Self { - let mut result = Self(Default::default()); + let mut result = Self([Rlimit::INFINITY; RLIM_NLIMITS as usize]); + + // x-kernel currently maps a fixed-size user stack and uses a fixed-capacity + // fd table, so keep those hard caps at the kernel-supported maximum instead + // of Linux's larger growable defaults. + // + // If we later add Linux-style stack growth or a resizable fd table, we + // should revisit these two entries and align their hard limits with the + // Linux defaults we report through `prlimit64`. result[RLIMIT_STACK] = (user_stack_size as u64).into(); + result[RLIMIT_CORE] = Rlimit::new(0, RLIM_INFINITY); + result[RLIMIT_NPROC] = Rlimit::new(0, 0); result[RLIMIT_NOFILE] = (FILE_LIMIT as u64).into(); + result[RLIMIT_MEMLOCK] = Rlimit::new(MLOCK_LIMIT_BYTES, MLOCK_LIMIT_BYTES); + result[RLIMIT_MSGQUEUE] = Rlimit::new(MSGQUEUE_LIMIT_BYTES, MSGQUEUE_LIMIT_BYTES); + result[RLIMIT_SIGPENDING] = Rlimit::new(0, 0); + result[RLIMIT_NICE] = Rlimit::new(0, 0); + result[RLIMIT_RTPRIO] = Rlimit::new(0, 0); result } } @@ -93,6 +118,8 @@ mod tests { let limits = Rlimits::new(0x80000); assert_eq!(limits[RLIMIT_STACK].current, 0x80000); assert_eq!(limits[RLIMIT_NOFILE].current, FILE_LIMIT as u64); + assert_eq!(limits[linux_raw_sys::general::RLIMIT_CPU], Rlimit::INFINITY); + assert_eq!(limits[RLIMIT_CORE], Rlimit::new(0, RLIM_INFINITY)); } #[def_test] @@ -110,5 +137,13 @@ mod tests { let limits = Rlimits::new(0x80000); assert_eq!(limits[RLIMIT_STACK].max, 0x80000); assert_eq!(limits[RLIMIT_NOFILE].max, FILE_LIMIT as u64); + assert_eq!( + limits[RLIMIT_MEMLOCK], + Rlimit::new(MLOCK_LIMIT_BYTES, MLOCK_LIMIT_BYTES) + ); + assert_eq!( + limits[RLIMIT_MSGQUEUE], + Rlimit::new(MSGQUEUE_LIMIT_BYTES, MSGQUEUE_LIMIT_BYTES) + ); } } -- Gitee