diff --git a/Cargo.lock b/Cargo.lock index 88b56991..9103dd88 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -306,6 +306,7 @@ dependencies = [ "paste", "rand", "ringbuf", + "rustc-hash", "tock-registers", ] @@ -456,6 +457,12 @@ dependencies = [ "portable-atomic-util", ] +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + [[package]] name = "rustc-std-workspace-core" version = "1.0.1" diff --git a/Cargo.toml b/Cargo.toml index f8feab70..11a9e1a9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,7 @@ ringbuf = { version = "0.4.8", default-features = false, features = ["alloc"] } bitflags = "2.9.1" futures = { version = "0.3.31", default-features = false, features = ["alloc", "async-await"] } rand = { version = "0.9.2", default-features = false, features = ["small_rng"] } +rustc-hash = { version = "2.1", default-features = false } [features] default = ["smp"] diff --git a/libkernel/src/fs/mod.rs b/libkernel/src/fs/mod.rs index 0b963b27..eb7294ad 100644 --- a/libkernel/src/fs/mod.rs +++ b/libkernel/src/fs/mod.rs @@ -322,3 +322,78 @@ pub trait Inode: Send + Sync + Any { Ok(()) } } + +/// A simplified trait for read-only files in procfs/sysfs that provides default implementations +/// for common inode operations. +#[async_trait] +pub trait SimpleFile { + fn id(&self) -> InodeId; + async fn getattr(&self) -> Result; + async fn read(&self) -> Result>; + async fn readlink(&self) -> Result { + Err(KernelError::NotSupported) + } +} + +#[async_trait] +impl Inode for T +where + T: SimpleFile + Send + Sync + 'static, +{ + fn id(&self) -> InodeId { + self.id() + } + + async fn read_at(&self, offset: u64, buf: &mut [u8]) -> Result { + let bytes = self.read().await?; + let end = usize::min(bytes.len().saturating_sub(offset as usize), buf.len()); + if end == 0 { + return Ok(0); + } + let slice = &bytes[offset as usize..offset as usize + end]; + buf[..end].copy_from_slice(slice); + Ok(end) + } + + async fn getattr(&self) -> Result { + self.getattr().await + } + + async fn lookup(&self, _name: &str) -> Result> { + Err(FsError::NotADirectory.into()) + } + + async fn readdir(&self, _start_offset: u64) -> crate::error::Result> { + Err(FsError::NotADirectory.into()) + } + + async fn readlink(&self) -> Result { + self.readlink().await + } +} + +pub struct SimpleDirStream { + entries: Vec, + idx: usize, +} + +impl SimpleDirStream { + pub fn new(entries: Vec, start_offset: u64) -> Self { + Self { + entries, + idx: start_offset as usize, + } + } +} + +#[async_trait] +impl DirStream for SimpleDirStream { + async fn next_entry(&mut self) -> Result> { + Ok(if let Some(entry) = self.entries.get(self.idx).cloned() { + self.idx += 1; + Some(entry) + } else { + None + }) + } +} diff --git a/src/arch/arm64/exceptions/syscall.rs b/src/arch/arm64/exceptions/syscall.rs index 86574717..9f3edc66 100644 --- a/src/arch/arm64/exceptions/syscall.rs +++ b/src/arch/arm64/exceptions/syscall.rs @@ -91,6 +91,8 @@ use libkernel::{ }; pub async fn handle_syscall() { + current_task().update_accounting(None); + current_task().in_syscall = true; ptrace_stop(TracePoint::SyscallEntry).await; let (nr, arg1, arg2, arg3, arg4, arg5, arg6) = { @@ -614,4 +616,6 @@ pub async fn handle_syscall() { current_task().ctx.user_mut().x[0] = ret_val.cast_unsigned() as u64; ptrace_stop(TracePoint::SyscallExit).await; + current_task().update_accounting(None); + current_task().in_syscall = false; } diff --git a/src/clock/gettime.rs b/src/clock/gettime.rs index 15e9a2e4..4ccdc902 100644 --- a/src/clock/gettime.rs +++ b/src/clock/gettime.rs @@ -1,21 +1,40 @@ +use core::sync::atomic::Ordering; +use core::time::Duration; use libkernel::{ error::{KernelError, Result}, memory::address::TUA, }; +use super::{ClockId, realtime::date, timespec::TimeSpec}; +use crate::drivers::timer::{Instant, now}; +use crate::sched::current::current_task_shared; use crate::{drivers::timer::uptime, memory::uaccess::copy_to_user}; -use super::{realtime::date, timespec::TimeSpec}; - -pub type ClockId = i32; - -const CLOCK_MONOTONIC: ClockId = 0; -const CLOCK_REALTIME: ClockId = 1; - -pub async fn sys_clock_gettime(clockid: ClockId, time_spec: TUA) -> Result { - let time = match clockid { - CLOCK_MONOTONIC => uptime(), - CLOCK_REALTIME => date(), +pub async fn sys_clock_gettime(clockid: i32, time_spec: TUA) -> Result { + let time = match ClockId::try_from(clockid).map_err(|_| KernelError::InvalidValue)? { + ClockId::Monotonic => uptime(), + ClockId::Realtime => date(), + ClockId::ProcessCpuTimeId => { + let task = current_task_shared(); + let total_time = task.process.stime.load(Ordering::Relaxed) as u64 + + task.process.utime.load(Ordering::Relaxed) as u64; + let last_update = Instant::from_user_normalized( + task.process.last_account.load(Ordering::Relaxed) as u64, + ); + let now = now().unwrap(); + let delta = now - last_update; + Duration::from(Instant::from_user_normalized(total_time)) + delta + } + ClockId::ThreadCpuTimeId => { + let task = current_task_shared(); + let total_time = task.stime.load(Ordering::Relaxed) as u64 + + task.utime.load(Ordering::Relaxed) as u64; + let last_update = + Instant::from_user_normalized(task.last_account.load(Ordering::Relaxed) as u64); + let now = now().unwrap(); + let delta = now - last_update; + Duration::from(Instant::from_user_normalized(total_time)) + delta + } _ => return Err(KernelError::InvalidValue), }; diff --git a/src/clock/mod.rs b/src/clock/mod.rs index 58ef00db..82ee8187 100644 --- a/src/clock/mod.rs +++ b/src/clock/mod.rs @@ -2,3 +2,38 @@ pub mod gettime; pub mod realtime; pub mod timeofday; pub mod timespec; + +pub enum ClockId { + Monotonic = 0, + Realtime = 1, + ProcessCpuTimeId = 2, + ThreadCpuTimeId = 3, + MonotonicRaw = 4, + RealtimeCoarse = 5, + MonotonicCoarse = 6, + BootTime = 7, + RealtimeAlarm = 8, + BootTimeAlarm = 9, + Tai = 11, +} + +impl TryFrom for ClockId { + type Error = (); + + fn try_from(value: i32) -> Result { + match value { + 0 => Ok(ClockId::Monotonic), + 1 => Ok(ClockId::Realtime), + 2 => Ok(ClockId::ProcessCpuTimeId), + 3 => Ok(ClockId::ThreadCpuTimeId), + 4 => Ok(ClockId::MonotonicRaw), + 5 => Ok(ClockId::RealtimeCoarse), + 6 => Ok(ClockId::MonotonicCoarse), + 7 => Ok(ClockId::BootTime), + 8 => Ok(ClockId::RealtimeAlarm), + 9 => Ok(ClockId::BootTimeAlarm), + 11 => Ok(ClockId::Tai), + _ => Err(()), + } + } +} diff --git a/src/drivers/fs/proc.rs b/src/drivers/fs/proc.rs index 43908ea3..73363291 100644 --- a/src/drivers/fs/proc.rs +++ b/src/drivers/fs/proc.rs @@ -1,45 +1,41 @@ #![allow(clippy::module_name_repetitions)] -use crate::process::find_task_by_descriptor; -use crate::process::thread_group::Tgid; -use crate::sched::current::current_task; +mod root; +mod task; + +use crate::drivers::{Driver, FilesystemDriver}; use crate::sync::OnceLock; -use crate::{ - drivers::{Driver, FilesystemDriver}, - process::TASK_LIST, -}; -use alloc::string::String; -use alloc::{boxed::Box, format, string::ToString, sync::Arc, vec::Vec}; +use alloc::{boxed::Box, sync::Arc}; use async_trait::async_trait; -use core::sync::atomic::{AtomicU64, Ordering}; -use libkernel::fs::pathbuf::PathBuf; +use core::hash::Hasher; use libkernel::{ - error::{FsError, KernelError, Result}, - fs::{ - BlockDevice, DirStream, Dirent, FileType, Filesystem, Inode, InodeId, PROCFS_ID, - attr::{FileAttr, FilePermissions}, - }, + error::{KernelError, Result}, + fs::{BlockDevice, Filesystem, Inode, PROCFS_ID}, }; use log::warn; +use root::ProcRootInode; + +/// Deterministically generates an inode ID for the given path segments within the procfs filesystem. +fn get_inode_id(path_segments: &[&str]) -> u64 { + let mut hasher = rustc_hash::FxHasher::default(); + // Ensure non-collision if other filesystems also use this method + hasher.write(b"procfs"); + for segment in path_segments { + hasher.write(segment.as_bytes()); + } + let hash = hasher.finish(); + assert_ne!(hash, 0, "Generated inode ID cannot be zero"); + hash +} pub struct ProcFs { root: Arc, - next_inode_id: AtomicU64, } impl ProcFs { fn new() -> Arc { let root_inode = Arc::new(ProcRootInode::new()); - Arc::new(Self { - root: root_inode, - next_inode_id: AtomicU64::new(1), - }) - } - - /// Convenience helper to allocate a unique inode ID inside this filesystem. - fn alloc_inode_id(&self) -> InodeId { - let id = self.next_inode_id.fetch_add(1, Ordering::Relaxed); - InodeId::from_fsid_and_inodeid(PROCFS_ID, id) + Arc::new(Self { root: root_inode }) } } @@ -54,392 +50,6 @@ impl Filesystem for ProcFs { } } -struct ProcDirStream { - entries: Vec, - idx: usize, -} - -#[async_trait] -impl DirStream for ProcDirStream { - async fn next_entry(&mut self) -> Result> { - Ok(if let Some(entry) = self.entries.get(self.idx).cloned() { - self.idx += 1; - Some(entry) - } else { - None - }) - } -} - -struct ProcRootInode { - id: InodeId, - attr: FileAttr, -} - -impl ProcRootInode { - fn new() -> Self { - Self { - id: InodeId::from_fsid_and_inodeid(PROCFS_ID, 0), - attr: FileAttr { - file_type: FileType::Directory, - mode: FilePermissions::from_bits_retain(0o555), - ..FileAttr::default() - }, - } - } -} - -#[async_trait] -impl Inode for ProcRootInode { - fn id(&self) -> InodeId { - self.id - } - - async fn lookup(&self, name: &str) -> Result> { - // Lookup a PID directory. - let pid = if name == "self" { - let current_task = current_task(); - current_task.descriptor().tgid() - } else { - Tgid( - name.parse() - .map_err(|_| FsError::NotFound) - .map_err(Into::::into)?, - ) - }; - - // Validate that the process actually exists. - if !TASK_LIST.lock_save_irq().keys().any(|d| d.tgid() == pid) { - return Err(FsError::NotFound.into()); - } - - let fs = procfs(); - - let inode_id = fs.alloc_inode_id(); - Ok(Arc::new(ProcTaskInode::new(pid, inode_id))) - } - - async fn getattr(&self) -> Result { - Ok(self.attr.clone()) - } - - async fn readdir(&self, start_offset: u64) -> Result> { - let mut entries: Vec = Vec::new(); - // Gather task list under interrupt-safe lock. - let task_list = TASK_LIST.lock_save_irq(); - for (idx, (desc, _)) in task_list - .iter() - .filter(|(_, task)| task.upgrade().is_some()) - .enumerate() - { - // Use offset index as dirent offset. - let name = desc.tgid().value().to_string(); - let inode_id = - InodeId::from_fsid_and_inodeid(PROCFS_ID, ((desc.tgid().0 + 1) * 100) as u64); - entries.push(Dirent::new( - name, - inode_id, - FileType::Directory, - (idx + 1) as u64, - )); - } - let current_task = current_task(); - entries.push(Dirent::new( - "self".to_string(), - InodeId::from_fsid_and_inodeid( - PROCFS_ID, - ((current_task.process.tgid.0 + 1) * 100) as u64, - ), - FileType::Directory, - (entries.len() + 1) as u64, - )); - - // honour start_offset - let entries = entries.into_iter().skip(start_offset as usize).collect(); - - Ok(Box::new(ProcDirStream { entries, idx: 0 })) - } -} - -struct ProcTaskInode { - id: InodeId, - attr: FileAttr, - pid: Tgid, -} - -impl ProcTaskInode { - fn new(pid: Tgid, inode_id: InodeId) -> Self { - Self { - id: inode_id, - attr: FileAttr { - file_type: FileType::Directory, - mode: FilePermissions::from_bits_retain(0o555), - ..FileAttr::default() - }, - pid, - } - } -} - -#[async_trait] -impl Inode for ProcTaskInode { - fn id(&self) -> InodeId { - self.id - } - - async fn lookup(&self, name: &str) -> Result> { - if let Ok(file_type) = TaskFileType::try_from(name) { - let fs = procfs(); - let inode_id = fs.alloc_inode_id(); - Ok(Arc::new(ProcTaskFileInode::new( - self.pid, file_type, inode_id, - ))) - } else { - Err(FsError::NotFound.into()) - } - } - - async fn getattr(&self) -> Result { - Ok(self.attr.clone()) - } - - async fn readdir(&self, start_offset: u64) -> Result> { - let mut entries: Vec = Vec::new(); - let inode_offset = ((self.pid.value() + 1) * 100) as u64; - entries.push(Dirent::new( - "status".to_string(), - InodeId::from_fsid_and_inodeid(PROCFS_ID, inode_offset + 1), - FileType::File, - 1, - )); - entries.push(Dirent::new( - "comm".to_string(), - InodeId::from_fsid_and_inodeid(PROCFS_ID, inode_offset + 2), - FileType::File, - 2, - )); - entries.push(Dirent::new( - "state".to_string(), - InodeId::from_fsid_and_inodeid(PROCFS_ID, inode_offset + 3), - FileType::File, - 3, - )); - entries.push(Dirent::new( - "cwd".to_string(), - InodeId::from_fsid_and_inodeid(PROCFS_ID, inode_offset + 4), - FileType::Symlink, - 4, - )); - entries.push(Dirent::new( - "stat".to_string(), - InodeId::from_fsid_and_inodeid(PROCFS_ID, inode_offset + 5), - FileType::File, - 5, - )); - - // honour start_offset - let entries = entries.into_iter().skip(start_offset as usize).collect(); - - Ok(Box::new(ProcDirStream { entries, idx: 0 })) - } -} - -enum TaskFileType { - Status, - Comm, - Cwd, - State, - Stat, -} - -impl TryFrom<&str> for TaskFileType { - type Error = (); - - fn try_from(value: &str) -> core::result::Result { - match value { - "status" => Ok(TaskFileType::Status), - "comm" => Ok(TaskFileType::Comm), - "state" => Ok(TaskFileType::State), - "stat" => Ok(TaskFileType::Stat), - "cwd" => Ok(TaskFileType::Cwd), - _ => Err(()), - } - } -} - -struct ProcTaskFileInode { - id: InodeId, - file_type: TaskFileType, - attr: FileAttr, - pid: Tgid, -} - -impl ProcTaskFileInode { - fn new(pid: Tgid, file_type: TaskFileType, inode_id: InodeId) -> Self { - Self { - id: inode_id, - attr: FileAttr { - file_type: match file_type { - TaskFileType::Status - | TaskFileType::Comm - | TaskFileType::State - | TaskFileType::Stat => FileType::File, - TaskFileType::Cwd => FileType::Symlink, - }, - mode: FilePermissions::from_bits_retain(0o444), - ..FileAttr::default() - }, - pid, - file_type, - } - } -} - -#[async_trait] -impl Inode for ProcTaskFileInode { - fn id(&self) -> InodeId { - self.id - } - - async fn lookup(&self, _name: &str) -> Result> { - Err(FsError::NotADirectory.into()) - } - - async fn getattr(&self) -> Result { - Ok(self.attr.clone()) - } - - async fn readdir(&self, _start_offset: u64) -> Result> { - Err(FsError::NotADirectory.into()) - } - - async fn read_at(&self, offset: u64, buf: &mut [u8]) -> Result { - let pid = self.pid; - // TODO: task needs to be the main thread of the process - let task_list = TASK_LIST.lock_save_irq(); - let id = task_list - .iter() - .find(|(desc, _)| desc.tgid() == pid) - .map(|(desc, _)| *desc); - drop(task_list); - let task_details = if let Some(desc) = id { - find_task_by_descriptor(&desc) - } else { - None - }; - - let status_string = if let Some(task) = task_details { - let state = *task.state.lock_save_irq(); - let name = task.comm.lock_save_irq(); - match self.file_type { - TaskFileType::Status => format!( - "Name:\t{name} -State:\t{state} -Tgid:\t{tgid} -FDSize:\t{fd_size} -Pid:\t{pid} -Threads:\t{tasks}\n", - name = name.as_str(), - tgid = task.process.tgid, - fd_size = task.fd_table.lock_save_irq().len(), - tasks = task.process.tasks.lock_save_irq().len(), - ), - TaskFileType::Comm => format!("{name}\n", name = name.as_str()), - TaskFileType::State => format!("{state}\n"), - TaskFileType::Stat => { - let mut output = String::new(); - output.push_str(&format!("{} ", task.process.tgid.0)); // pid - output.push_str(&format!("({}) ", name.as_str())); // comm - output.push_str(&format!("{state} ")); // state - output.push_str(&format!("{} ", 0)); // ppid - output.push_str(&format!("{} ", 0)); // pgrp - output.push_str(&format!("{} ", task.process.sid.lock_save_irq().value())); // session - output.push_str(&format!("{} ", 0)); // tty_nr - output.push_str(&format!("{} ", 0)); // tpgid - output.push_str(&format!("{} ", 0)); // flags - output.push_str(&format!("{} ", 0)); // minflt - output.push_str(&format!("{} ", 0)); // cminflt - output.push_str(&format!("{} ", 0)); // majflt - output.push_str(&format!("{} ", 0)); // cmajflt - output.push_str(&format!("{} ", task.process.utime.load(Ordering::Relaxed))); // utime - output.push_str(&format!("{} ", task.process.stime.load(Ordering::Relaxed))); // stime - output.push_str(&format!("{} ", 0)); // cutime - output.push_str(&format!("{} ", 0)); // cstime - output.push_str(&format!("{} ", *task.process.priority.lock_save_irq())); // priority - output.push_str(&format!("{} ", 0)); // nice - output.push_str(&format!("{} ", 0)); // num_threads - output.push_str(&format!("{} ", 0)); // itrealvalue - output.push_str(&format!("{} ", 0)); // starttime - output.push_str(&format!("{} ", 0)); // vsize - output.push_str(&format!("{} ", 0)); // rss - output.push_str(&format!("{} ", 0)); // rsslim - output.push_str(&format!("{} ", 0)); // startcode - output.push_str(&format!("{} ", 0)); // endcode - output.push_str(&format!("{} ", 0)); // startstack - output.push_str(&format!("{} ", 0)); // kstkesp - output.push_str(&format!("{} ", 0)); // kstkeip - output.push_str(&format!("{} ", 0)); // signal - output.push_str(&format!("{} ", 0)); // blocked - output.push_str(&format!("{} ", 0)); // sigignore - output.push_str(&format!("{} ", 0)); // sigcatch - output.push_str(&format!("{} ", 0)); // wchan - output.push_str(&format!("{} ", 0)); // nswap - output.push_str(&format!("{} ", 0)); // cnswap - output.push_str(&format!("{} ", 0)); // exit_signal - output.push_str(&format!("{} ", 0)); // processor - output.push_str(&format!("{} ", 0)); // rt_priority - output.push_str(&format!("{} ", 0)); // policy - output.push_str(&format!("{} ", 0)); // delayacct_blkio_ticks - output.push_str(&format!("{} ", 0)); // guest_time - output.push_str(&format!("{} ", 0)); // cguest_time - output.push_str(&format!("{} ", 0)); // start_data - output.push_str(&format!("{} ", 0)); // end_data - output.push_str(&format!("{} ", 0)); // start_brk - output.push_str(&format!("{} ", 0)); // arg_start - output.push_str(&format!("{} ", 0)); // arg_end - output.push_str(&format!("{} ", 0)); // env_start - output.push_str(&format!("{} ", 0)); // env_end - output.push_str(&format!("{} ", 0)); // exit_code - output.push('\n'); - output - } - TaskFileType::Cwd => task.cwd.lock_save_irq().clone().1.as_str().to_string(), - } - } else { - "State:\tGone\n".to_string() - }; - - let bytes = status_string.as_bytes(); - let end = usize::min(bytes.len().saturating_sub(offset as usize), buf.len()); - if end == 0 { - return Ok(0); - } - let slice = &bytes[offset as usize..offset as usize + end]; - buf[..end].copy_from_slice(slice); - Ok(end) - } - - async fn readlink(&self) -> Result { - if let TaskFileType::Cwd = self.file_type { - let pid = self.pid; - let task_list = TASK_LIST.lock_save_irq(); - let id = task_list.iter().find(|(desc, _)| desc.tgid() == pid); - let task_details = if let Some((desc, _)) = id { - find_task_by_descriptor(desc) - } else { - None - }; - return if let Some(task) = task_details { - let cwd = task.cwd.lock_save_irq(); - Ok(cwd.1.clone()) - } else { - Err(FsError::NotFound.into()) - }; - } - Err(KernelError::NotSupported) - } -} - static PROCFS_INSTANCE: OnceLock> = OnceLock::new(); /// Initializes and/or returns the global singleton [`ProcFs`] instance. diff --git a/src/drivers/fs/proc/root.rs b/src/drivers/fs/proc/root.rs new file mode 100644 index 00000000..8e3c571d --- /dev/null +++ b/src/drivers/fs/proc/root.rs @@ -0,0 +1,112 @@ +use crate::drivers::fs::proc::get_inode_id; +use crate::drivers::fs::proc::task::ProcTaskInode; +use crate::process::{TASK_LIST, TaskDescriptor, Tid}; +use crate::sched::current::current_task; +use alloc::boxed::Box; +use alloc::string::ToString; +use alloc::sync::Arc; +use alloc::vec::Vec; +use async_trait::async_trait; +use libkernel::error; +use libkernel::error::FsError; +use libkernel::fs::attr::{FileAttr, FilePermissions}; +use libkernel::fs::{DirStream, Dirent, FileType, Inode, InodeId, PROCFS_ID, SimpleDirStream}; + +pub struct ProcRootInode { + id: InodeId, + attr: FileAttr, +} + +impl ProcRootInode { + pub fn new() -> Self { + Self { + id: InodeId::from_fsid_and_inodeid(PROCFS_ID, 0), + attr: FileAttr { + file_type: FileType::Directory, + mode: FilePermissions::from_bits_retain(0o555), + ..FileAttr::default() + }, + } + } +} + +#[async_trait] +impl Inode for ProcRootInode { + fn id(&self) -> InodeId { + self.id + } + + async fn lookup(&self, name: &str) -> error::Result> { + // Lookup a PID directory. + let desc = if name == "self" { + let current_task = current_task(); + TaskDescriptor::from_tgid_tid(current_task.pgid(), Tid::from_tgid(current_task.pgid())) + } else if name == "thread-self" { + let current_task = current_task(); + current_task.descriptor() + } else { + let pid: u32 = name.parse().map_err(|_| FsError::NotFound)?; + // Search for the task descriptor. + TASK_LIST + .lock_save_irq() + .keys() + .find(|d| d.tgid().value() == pid) + .cloned() + .ok_or(FsError::NotFound)? + }; + + Ok(Arc::new(ProcTaskInode::new( + desc, + false, + InodeId::from_fsid_and_inodeid(self.id.fs_id(), get_inode_id(&[name])), + ))) + } + + async fn getattr(&self) -> error::Result { + Ok(self.attr.clone()) + } + + async fn readdir(&self, start_offset: u64) -> error::Result> { + let mut entries: Vec = Vec::new(); + // Gather task list under interrupt-safe lock. + let task_list = TASK_LIST.lock_save_irq(); + for (desc, _) in task_list + .iter() + .filter(|(_, task)| task.upgrade().is_some()) + { + let name = desc.tgid().value().to_string(); + let inode_id = InodeId::from_fsid_and_inodeid( + PROCFS_ID, + get_inode_id(&[&desc.tgid().value().to_string()]), + ); + let next_offset = (entries.len() + 1) as u64; + entries.push(Dirent::new( + name, + inode_id, + FileType::Directory, + next_offset, + )); + } + let current_task = current_task(); + entries.push(Dirent::new( + "self".to_string(), + InodeId::from_fsid_and_inodeid( + PROCFS_ID, + get_inode_id(&[¤t_task.descriptor().tgid().value().to_string()]), + ), + FileType::Directory, + (entries.len() + 1) as u64, + )); + entries.push(Dirent::new( + "thread-self".to_string(), + InodeId::from_fsid_and_inodeid( + PROCFS_ID, + get_inode_id(&[¤t_task.descriptor().tid().value().to_string()]), + ), + FileType::Directory, + (entries.len() + 1) as u64, + )); + + Ok(Box::new(SimpleDirStream::new(entries, start_offset))) + } +} diff --git a/src/drivers/fs/proc/task/fd.rs b/src/drivers/fs/proc/task/fd.rs new file mode 100644 index 00000000..781c713b --- /dev/null +++ b/src/drivers/fs/proc/task/fd.rs @@ -0,0 +1,154 @@ +use crate::drivers::fs::proc::{get_inode_id, procfs}; +use crate::process::fd_table::Fd; +use crate::process::{TaskDescriptor, find_task_by_descriptor}; +use crate::sched::current::current_task_shared; +use alloc::boxed::Box; +use alloc::format; +use alloc::string::ToString; +use alloc::sync::Arc; +use alloc::vec::Vec; +use async_trait::async_trait; +use libkernel::error::Result; +use libkernel::error::{FsError, KernelError}; +use libkernel::fs::attr::FileAttr; +use libkernel::fs::{ + DirStream, Dirent, FileType, Filesystem, Inode, InodeId, SimpleDirStream, SimpleFile, +}; + +pub struct ProcFdInode { + id: InodeId, + attr: FileAttr, + desc: TaskDescriptor, + fd_info: bool, +} + +impl ProcFdInode { + pub fn new(desc: TaskDescriptor, fd_info: bool, inode_id: InodeId) -> Self { + Self { + id: inode_id, + attr: FileAttr { + file_type: FileType::Directory, + // Define appropriate file attributes for fdinfo. + ..FileAttr::default() + }, + desc, + fd_info, + } + } + + fn dir_name(&self) -> &str { + if self.fd_info { "fdinfo" } else { "fd" } + } +} + +#[async_trait] +impl Inode for ProcFdInode { + fn id(&self) -> InodeId { + self.id + } + + async fn getattr(&self) -> Result { + Ok(self.attr.clone()) + } + + async fn lookup(&self, name: &str) -> Result> { + let fd: i32 = name.parse().map_err(|_| FsError::NotFound)?; + let task = current_task_shared(); + let fd_table = task.fd_table.lock_save_irq(); + if fd_table.get(Fd(fd)).is_none() { + return Err(FsError::NotFound.into()); + } + let fs = procfs(); + let inode_id = InodeId::from_fsid_and_inodeid( + fs.id(), + get_inode_id(&[&self.desc.tid().value().to_string(), self.dir_name(), name]), + ); + Ok(Arc::new(ProcFdFile::new( + self.desc, + self.fd_info, + fd, + inode_id, + ))) + } + + async fn readdir(&self, start_offset: u64) -> Result> { + let task = find_task_by_descriptor(&self.desc).ok_or(FsError::NotFound)?; + let fd_table = task.fd_table.lock_save_irq(); + let mut entries = Vec::new(); + for fd in 0..fd_table.len() { + if fd_table.get(Fd(fd as i32)).is_none() { + continue; + } + let fd_str = fd.to_string(); + let next_offset = (entries.len() + 1) as u64; + entries.push(Dirent { + id: InodeId::from_fsid_and_inodeid( + self.id.fs_id(), + get_inode_id(&[ + &self.desc.tid().value().to_string(), + self.dir_name(), + &fd_str, + ]), + ), + offset: next_offset, + file_type: FileType::File, + name: fd_str, + }); + } + + Ok(Box::new(SimpleDirStream::new(entries, start_offset))) + } +} + +// TODO: Support fd links in /proc/[pid]/fd/ + +pub struct ProcFdFile { + id: InodeId, + attr: FileAttr, + desc: TaskDescriptor, + fd_info: bool, + fd: i32, +} + +impl ProcFdFile { + pub fn new(desc: TaskDescriptor, fd_info: bool, fd: i32, inode_id: InodeId) -> Self { + Self { + id: inode_id, + attr: FileAttr { + file_type: FileType::File, + // Define appropriate file attributes for fdinfo file. + ..FileAttr::default() + }, + desc, + fd_info, + fd, + } + } +} + +#[async_trait] +impl SimpleFile for ProcFdFile { + fn id(&self) -> InodeId { + self.id + } + + async fn getattr(&self) -> Result { + Ok(self.attr.clone()) + } + + async fn read(&self) -> Result> { + let task = find_task_by_descriptor(&self.desc).ok_or(FsError::NotFound)?; + let fd_entry = task + .fd_table + .lock_save_irq() + .get(Fd(self.fd)) + .ok_or(FsError::NotFound)?; + let (_, ctx) = &mut *fd_entry.lock().await; + let info_string = format!("pos: {}\nflags: {}", ctx.pos, ctx.flags.bits()); + if self.fd_info { + Ok(info_string.into_bytes()) + } else { + Err(KernelError::NotSupported) + } + } +} diff --git a/src/drivers/fs/proc/task/mod.rs b/src/drivers/fs/proc/task/mod.rs new file mode 100644 index 00000000..3487bd6e --- /dev/null +++ b/src/drivers/fs/proc/task/mod.rs @@ -0,0 +1,140 @@ +mod fd; +// TODO: allowlist this across the codebase +#[expect(clippy::module_inception)] +mod task; +mod task_file; + +use crate::drivers::fs::proc::task::task_file::{ProcTaskFileInode, TaskFileType}; +use crate::drivers::fs::proc::{get_inode_id, procfs}; +use crate::process::TaskDescriptor; +use alloc::boxed::Box; +use alloc::string::ToString; +use alloc::sync::Arc; +use alloc::vec::Vec; +use async_trait::async_trait; +use libkernel::error::FsError; +use libkernel::fs::attr::{FileAttr, FilePermissions}; +use libkernel::fs::{ + DirStream, Dirent, FileType, Filesystem, Inode, InodeId, PROCFS_ID, SimpleDirStream, +}; + +pub struct ProcTaskInode { + id: InodeId, + attr: FileAttr, + desc: TaskDescriptor, + is_task_dir: bool, +} + +impl ProcTaskInode { + pub fn new(desc: TaskDescriptor, is_task_dir: bool, inode_id: InodeId) -> Self { + Self { + id: inode_id, + attr: FileAttr { + file_type: FileType::Directory, + mode: FilePermissions::from_bits_retain(0o555), + ..FileAttr::default() + }, + desc, + is_task_dir, + } + } +} + +#[async_trait] +impl Inode for ProcTaskInode { + fn id(&self) -> InodeId { + self.id + } + + async fn lookup(&self, name: &str) -> libkernel::error::Result> { + let fs = procfs(); + let inode_id = InodeId::from_fsid_and_inodeid( + fs.id(), + get_inode_id(&[&self.desc.tid().value().to_string(), name]), + ); + if name == "fdinfo" { + return Ok(Arc::new(fd::ProcFdInode::new(self.desc, true, inode_id))); + } else if name == "fd" { + return Ok(Arc::new(fd::ProcFdInode::new(self.desc, false, inode_id))); + } else if name == "task" + && self.desc.tid().value() == self.desc.tgid().value() + && !self.is_task_dir + { + return Ok(Arc::new(task::ProcTaskDirInode::new( + self.desc.tgid(), + inode_id, + ))); + } + if let Ok(file_type) = TaskFileType::try_from(name) { + Ok(Arc::new(ProcTaskFileInode::new( + self.desc.tid(), + file_type, + self.is_task_dir, + inode_id, + ))) + } else { + Err(FsError::NotFound.into()) + } + } + + async fn getattr(&self) -> libkernel::error::Result { + Ok(self.attr.clone()) + } + + async fn readdir(&self, start_offset: u64) -> libkernel::error::Result> { + let mut entries: Vec = Vec::new(); + let initial_str = self.desc.tid().value().to_string(); + entries.push(Dirent::new( + "status".to_string(), + InodeId::from_fsid_and_inodeid(PROCFS_ID, get_inode_id(&[&initial_str, "status"])), + FileType::File, + 1, + )); + entries.push(Dirent::new( + "comm".to_string(), + InodeId::from_fsid_and_inodeid(PROCFS_ID, get_inode_id(&[&initial_str, "comm"])), + FileType::File, + 2, + )); + entries.push(Dirent::new( + "state".to_string(), + InodeId::from_fsid_and_inodeid(PROCFS_ID, get_inode_id(&[&initial_str, "state"])), + FileType::File, + 3, + )); + entries.push(Dirent::new( + "cwd".to_string(), + InodeId::from_fsid_and_inodeid(PROCFS_ID, get_inode_id(&[&initial_str, "cwd"])), + FileType::Symlink, + 4, + )); + entries.push(Dirent::new( + "stat".to_string(), + InodeId::from_fsid_and_inodeid(PROCFS_ID, get_inode_id(&[&initial_str, "stat"])), + FileType::File, + 5, + )); + entries.push(Dirent::new( + "fd".to_string(), + InodeId::from_fsid_and_inodeid(PROCFS_ID, get_inode_id(&[&initial_str, "fd"])), + FileType::Directory, + 6, + )); + entries.push(Dirent::new( + "fdinfo".to_string(), + InodeId::from_fsid_and_inodeid(PROCFS_ID, get_inode_id(&[&initial_str, "fdinfo"])), + FileType::Directory, + 7, + )); + if self.desc.tid().value() == self.desc.tgid().value() && !self.is_task_dir { + entries.push(Dirent::new( + "task".to_string(), + InodeId::from_fsid_and_inodeid(PROCFS_ID, get_inode_id(&[&initial_str, "task"])), + FileType::Directory, + 8, + )); + } + + Ok(Box::new(SimpleDirStream::new(entries, start_offset))) + } +} diff --git a/src/drivers/fs/proc/task/task.rs b/src/drivers/fs/proc/task/task.rs new file mode 100644 index 00000000..128c0dfd --- /dev/null +++ b/src/drivers/fs/proc/task/task.rs @@ -0,0 +1,87 @@ +use crate::drivers::fs::proc::task::ProcTaskInode; +use crate::drivers::fs::proc::{get_inode_id, procfs}; +use crate::process::thread_group::Tgid; +use crate::process::{TaskDescriptor, Tid, find_task_by_descriptor}; +use alloc::boxed::Box; +use alloc::string::ToString; +use alloc::sync::Arc; +use alloc::vec::Vec; +use async_trait::async_trait; +use libkernel::error::FsError; +use libkernel::fs::attr::FileAttr; +use libkernel::fs::{DirStream, Dirent, FileType, Filesystem, Inode, InodeId, SimpleDirStream}; + +pub struct ProcTaskDirInode { + id: InodeId, + attr: FileAttr, + tgid: Tgid, +} + +impl ProcTaskDirInode { + pub fn new(tgid: Tgid, inode_id: InodeId) -> Self { + Self { + id: inode_id, + attr: FileAttr { + file_type: FileType::Directory, + // Define appropriate file attributes for fdinfo. + ..FileAttr::default() + }, + tgid, + } + } +} + +#[async_trait] +impl Inode for ProcTaskDirInode { + fn id(&self) -> InodeId { + self.id + } + + async fn getattr(&self) -> libkernel::error::Result { + Ok(self.attr.clone()) + } + + async fn lookup(&self, name: &str) -> libkernel::error::Result> { + let tid = match name.parse::() { + Ok(tid) => Tid(tid), + Err(_) => return Err(FsError::NotFound.into()), + }; + let fs = procfs(); + let inode_id = InodeId::from_fsid_and_inodeid( + fs.id(), + get_inode_id(&[&self.tgid.value().to_string(), &tid.value().to_string()]), + ); + let desc = TaskDescriptor::from_tgid_tid(self.tgid, tid); + find_task_by_descriptor(&desc).ok_or(FsError::NotFound)?; + Ok(Arc::new(ProcTaskInode::new(desc, true, inode_id))) + } + + async fn readdir(&self, start_offset: u64) -> libkernel::error::Result> { + let task = find_task_by_descriptor(&TaskDescriptor::from_tgid_tid( + self.tgid, + Tid::from_tgid(self.tgid), + )) + .ok_or(FsError::NotFound)?; + let tasks = task.process.tasks.lock_save_irq(); + let mut entries = Vec::new(); + for (i, (_tid, task)) in tasks.iter().enumerate().skip(start_offset as usize) { + let Some(task) = task.upgrade() else { + continue; + }; + let id = InodeId::from_fsid_and_inodeid( + procfs().id(), + get_inode_id(&[ + &self.tgid.value().to_string(), + &task.tid.value().to_string(), + ]), + ); + entries.push(Dirent { + id, + offset: (i + 1) as u64, + file_type: FileType::Directory, + name: task.tid.value().to_string(), + }); + } + Ok(Box::new(SimpleDirStream::new(entries, start_offset))) + } +} diff --git a/src/drivers/fs/proc/task/task_file.rs b/src/drivers/fs/proc/task/task_file.rs new file mode 100644 index 00000000..6209b9eb --- /dev/null +++ b/src/drivers/fs/proc/task/task_file.rs @@ -0,0 +1,226 @@ +use crate::process::{TASK_LIST, Tid, find_task_by_descriptor}; +use alloc::boxed::Box; +use alloc::format; +use alloc::string::{String, ToString}; +use alloc::vec::Vec; +use async_trait::async_trait; +use core::sync::atomic::Ordering; +use libkernel::error::{FsError, KernelError}; +use libkernel::fs::attr::{FileAttr, FilePermissions}; +use libkernel::fs::pathbuf::PathBuf; +use libkernel::fs::{FileType, InodeId, SimpleFile}; + +pub enum TaskFileType { + Status, + Comm, + Cwd, + Root, + State, + Stat, +} + +impl TryFrom<&str> for TaskFileType { + type Error = (); + + fn try_from(value: &str) -> Result { + match value { + "status" => Ok(TaskFileType::Status), + "comm" => Ok(TaskFileType::Comm), + "state" => Ok(TaskFileType::State), + "stat" => Ok(TaskFileType::Stat), + "cwd" => Ok(TaskFileType::Cwd), + "root" => Ok(TaskFileType::Root), + _ => Err(()), + } + } +} + +pub struct ProcTaskFileInode { + id: InodeId, + file_type: TaskFileType, + attr: FileAttr, + tid: Tid, + process_stats: bool, +} + +impl ProcTaskFileInode { + pub fn new(tid: Tid, file_type: TaskFileType, process_stats: bool, inode_id: InodeId) -> Self { + Self { + id: inode_id, + attr: FileAttr { + file_type: match file_type { + TaskFileType::Status + | TaskFileType::Comm + | TaskFileType::State + | TaskFileType::Stat => FileType::File, + TaskFileType::Cwd | TaskFileType::Root => FileType::Symlink, + }, + mode: FilePermissions::from_bits_retain(0o444), + ..FileAttr::default() + }, + process_stats, + tid, + file_type, + } + } +} + +#[async_trait] +impl SimpleFile for ProcTaskFileInode { + fn id(&self) -> InodeId { + self.id + } + + async fn getattr(&self) -> libkernel::error::Result { + Ok(self.attr.clone()) + } + + async fn read(&self) -> libkernel::error::Result> { + let tid = self.tid; + let task_list = TASK_LIST.lock_save_irq(); + let id = task_list + .iter() + .find(|(desc, _)| desc.tid() == tid) + .map(|(desc, _)| *desc); + drop(task_list); + let task_details = if let Some(desc) = id { + find_task_by_descriptor(&desc) + } else { + None + }; + + let status_string = if let Some(task) = task_details { + let state = *task.state.lock_save_irq(); + let name = task.comm.lock_save_irq(); + match self.file_type { + TaskFileType::Status => format!( + "Name:\t{name} +State:\t{state} +Tgid:\t{tgid} +FDSize:\t{fd_size} +Pid:\t{pid} +Threads:\t{tasks}\n", + name = name.as_str(), + tgid = task.process.tgid, + fd_size = task.fd_table.lock_save_irq().len(), + pid = task.tid.value(), + tasks = task.process.tasks.lock_save_irq().len(), + ), + TaskFileType::Comm => format!("{name}\n", name = name.as_str()), + TaskFileType::State => format!("{state}\n"), + TaskFileType::Stat => { + let mut output = String::new(); + output.push_str(&format!("{} ", task.process.tgid.value())); // pid + output.push_str(&format!("({}) ", name.as_str())); // comm + output.push_str(&format!("{state}")); // state + output.push_str(&format!("{} ", 0)); // ppid + output.push_str(&format!("{} ", 0)); // pgrp + output.push_str(&format!("{} ", task.process.sid.lock_save_irq().value())); // session + output.push_str(&format!("{} ", 0)); // tty_nr + output.push_str(&format!("{} ", 0)); // tpgid + output.push_str(&format!("{} ", 0)); // flags + output.push_str(&format!("{} ", 0)); // minflt + output.push_str(&format!("{} ", 0)); // cminflt + output.push_str(&format!("{} ", 0)); // majflt + output.push_str(&format!("{} ", 0)); // cmajflt + if self.process_stats { + output + .push_str(&format!("{} ", task.process.utime.load(Ordering::Relaxed))); // utime + output + .push_str(&format!("{} ", task.process.stime.load(Ordering::Relaxed))); // stime + } else { + output.push_str(&format!("{} ", task.utime.load(Ordering::Relaxed))); // utime + output.push_str(&format!("{} ", task.stime.load(Ordering::Relaxed))); // stime + } + output.push_str(&format!("{} ", 0)); // cutime + output.push_str(&format!("{} ", 0)); // cstime + output.push_str(&format!("{} ", *task.process.priority.lock_save_irq())); // priority + output.push_str(&format!("{} ", 0)); // nice + output.push_str(&format!("{} ", 0)); // num_threads + output.push_str(&format!("{} ", 0)); // itrealvalue + output.push_str(&format!("{} ", 0)); // starttime + output.push_str(&format!("{} ", 0)); // vsize + output.push_str(&format!("{} ", 0)); // rss + output.push_str(&format!("{} ", 0)); // rsslim + output.push_str(&format!("{} ", 0)); // startcode + output.push_str(&format!("{} ", 0)); // endcode + output.push_str(&format!("{} ", 0)); // startstack + output.push_str(&format!("{} ", 0)); // kstkesp + output.push_str(&format!("{} ", 0)); // kstkeip + output.push_str(&format!("{} ", 0)); // signal + output.push_str(&format!("{} ", 0)); // blocked + output.push_str(&format!("{} ", 0)); // sigignore + output.push_str(&format!("{} ", 0)); // sigcatch + output.push_str(&format!("{} ", 0)); // wchan + output.push_str(&format!("{} ", 0)); // nswap + output.push_str(&format!("{} ", 0)); // cnswap + output.push_str(&format!("{} ", 0)); // exit_signal + output.push_str(&format!("{} ", 0)); // processor + output.push_str(&format!("{} ", 0)); // rt_priority + output.push_str(&format!("{} ", 0)); // policy + output.push_str(&format!("{} ", 0)); // delayacct_blkio_ticks + output.push_str(&format!("{} ", 0)); // guest_time + output.push_str(&format!("{} ", 0)); // cguest_time + output.push_str(&format!("{} ", 0)); // start_data + output.push_str(&format!("{} ", 0)); // end_data + output.push_str(&format!("{} ", 0)); // start_brk + output.push_str(&format!("{} ", 0)); // arg_start + output.push_str(&format!("{} ", 0)); // arg_end + output.push_str(&format!("{} ", 0)); // env_start + output.push_str(&format!("{} ", 0)); // env_end + output.push_str(&format!("{} ", 0)); // exit_code + output.push('\n'); + output + } + TaskFileType::Cwd => task.cwd.lock_save_irq().clone().1.as_str().to_string(), + TaskFileType::Root => task.root.lock_save_irq().1.as_str().to_string(), + } + } else { + "State:\tGone\n".to_string() + }; + Ok(status_string.into_bytes()) + } + + async fn readlink(&self) -> libkernel::error::Result { + if let TaskFileType::Cwd = self.file_type { + let tid = self.tid; + let task_list = TASK_LIST.lock_save_irq(); + let id = task_list + .iter() + .find(|(desc, _)| desc.tid() == tid) + .map(|(desc, _)| *desc); + drop(task_list); + let task_details = if let Some(desc) = id { + find_task_by_descriptor(&desc) + } else { + None + }; + return if let Some(task) = task_details { + let cwd = task.cwd.lock_save_irq(); + Ok(cwd.1.clone()) + } else { + Err(FsError::NotFound.into()) + }; + } else if let TaskFileType::Root = self.file_type { + let tid = self.tid; + let task_list = TASK_LIST.lock_save_irq(); + let id = task_list + .iter() + .find(|(desc, _)| desc.tid() == tid) + .map(|(desc, _)| *desc); + drop(task_list); + let task_details = if let Some(desc) = id { + find_task_by_descriptor(&desc) + } else { + None + }; + return if let Some(task) = task_details { + let root = task.root.lock_save_irq(); + Ok(root.1.clone()) + } else { + Err(FsError::NotFound.into()) + }; + } + Err(KernelError::NotSupported) + } +} diff --git a/src/drivers/timer/mod.rs b/src/drivers/timer/mod.rs index 81e0f487..04be6d7c 100644 --- a/src/drivers/timer/mod.rs +++ b/src/drivers/timer/mod.rs @@ -12,6 +12,8 @@ use core::{ pub mod armv8_arch; +const USER_HZ: u64 = 100; + /// Represents a fixed point in monotonic time. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct Instant { @@ -19,6 +21,47 @@ pub struct Instant { freq: u64, } +impl Instant { + pub fn ticks(&self) -> u64 { + self.ticks + } + + pub fn freq(&self) -> u64 { + self.freq + } + + pub fn user_normalized(&self) -> Self { + // Userspace assumes everything runs at USER_HZ frequency. + if self.freq == USER_HZ { + *self + } else { + let ticks = (self.ticks as u128 * USER_HZ as u128) / self.freq as u128; + Self { + ticks: ticks as u64, + freq: USER_HZ, + } + } + } + + pub fn from_user_normalized(ticks: u64) -> Self { + Self { + ticks, + freq: USER_HZ, + } + } +} + +impl From for Duration { + fn from(instant: Instant) -> Self { + let secs = instant.ticks / instant.freq; + let remaining_ticks = instant.ticks % instant.freq; + + let nanos = ((remaining_ticks as u128 * 1_000_000_000) / instant.freq as u128) as u32; + + Duration::new(secs, nanos) + } +} + impl Ord for Instant { fn cmp(&self, other: &Self) -> core::cmp::Ordering { self.ticks.cmp(&other.ticks) diff --git a/src/main.rs b/src/main.rs index 0938e23f..7f689a74 100644 --- a/src/main.rs +++ b/src/main.rs @@ -134,8 +134,8 @@ async fn launch_init(mut opts: KOptions) { // Now that the root fs has been mounted, set the real root inode as the // cwd and root. - *task.cwd.lock_save_irq() = (VFS.root_inode(), PathBuf::new()); - *task.root.lock_save_irq() = (VFS.root_inode(), PathBuf::new()); + *task.cwd.lock_save_irq() = (VFS.root_inode(), PathBuf::from("/")); + *task.root.lock_save_irq() = (VFS.root_inode(), PathBuf::from("/")); let console = VFS .open( diff --git a/src/process/clone.rs b/src/process/clone.rs index 16846844..09acd9f7 100644 --- a/src/process/clone.rs +++ b/src/process/clone.rs @@ -10,6 +10,7 @@ use crate::{ }; use alloc::boxed::Box; use bitflags::bitflags; +use core::sync::atomic::AtomicUsize; use libkernel::memory::address::TUA; use libkernel::{ error::{KernelError, Result}, @@ -170,7 +171,11 @@ pub async fn sys_clone( state: Arc::new(SpinLock::new(TaskState::Runnable)), last_cpu: SpinLock::new(CpuId::this()), ptrace: SpinLock::new(ptrace), + utime: AtomicUsize::new(0), + stime: AtomicUsize::new(0), + last_account: AtomicUsize::new(0), }), + in_syscall: false, } }; diff --git a/src/process/mod.rs b/src/process/mod.rs index 966a8f61..43c4cdf1 100644 --- a/src/process/mod.rs +++ b/src/process/mod.rs @@ -1,3 +1,4 @@ +use crate::drivers::timer::Instant; use crate::{ arch::ArchImpl, kernel::cpu_id::CpuId, @@ -13,6 +14,7 @@ use alloc::{ sync::{Arc, Weak}, }; use core::fmt::Display; +use core::sync::atomic::AtomicUsize; use creds::Credentials; use fd_table::FileDescriptorTable; use libkernel::{ @@ -185,6 +187,9 @@ pub struct Task { pub state: Arc>, pub last_cpu: SpinLock, pub ptrace: SpinLock, + pub utime: AtomicUsize, + pub stime: AtomicUsize, + pub last_account: AtomicUsize, } impl Task { @@ -268,6 +273,47 @@ impl Task { } } } + + pub fn update_utime(&self, now: Instant) { + let now = now.user_normalized(); + let now = now.ticks() as usize; + let last_account = self + .last_account + .load(core::sync::atomic::Ordering::Relaxed); + let delta = now.saturating_sub(last_account); + self.utime + .fetch_add(delta, core::sync::atomic::Ordering::Relaxed); + self.process + .utime + .fetch_add(delta, core::sync::atomic::Ordering::Relaxed); + self.last_account + .store(now, core::sync::atomic::Ordering::Relaxed); + } + + pub fn update_stime(&self, now: Instant) { + let now = now.user_normalized(); + let now = now.ticks() as usize; + let last_account = self + .last_account + .load(core::sync::atomic::Ordering::Relaxed); + let delta = now.saturating_sub(last_account); + self.stime + .fetch_add(delta, core::sync::atomic::Ordering::Relaxed); + self.process + .stime + .fetch_add(delta, core::sync::atomic::Ordering::Relaxed); + self.last_account + .store(now, core::sync::atomic::Ordering::Relaxed); + } + + pub fn reset_last_account(&self, now: Instant) { + let now = now.user_normalized(); + let now = now.ticks() as usize; + self.last_account + .store(now, core::sync::atomic::Ordering::Relaxed); + self.last_account + .store(now, core::sync::atomic::Ordering::Relaxed); + } } pub fn find_task_by_descriptor(descriptor: &TaskDescriptor) -> Option> { diff --git a/src/process/owned.rs b/src/process/owned.rs index 287d5d86..fe2408dc 100644 --- a/src/process/owned.rs +++ b/src/process/owned.rs @@ -13,6 +13,7 @@ use super::{ }, threading::RobustListHead, }; +use crate::drivers::timer::{Instant, now}; use crate::{ arch::{Arch, ArchImpl}, fs::DummyInode, @@ -20,6 +21,7 @@ use crate::{ sync::SpinLock, }; use alloc::sync::Arc; +use core::sync::atomic::AtomicUsize; use libkernel::{ VirtualMemory, fs::pathbuf::PathBuf, @@ -39,6 +41,7 @@ pub struct OwnedTask { pub robust_list: Option>, pub child_tid_ptr: Option>, pub t_shared: Arc, + pub in_syscall: bool, } unsafe impl Send for OwnedTask {} @@ -77,6 +80,9 @@ impl OwnedTask { fd_table: Arc::new(SpinLock::new(FileDescriptorTable::new())), last_cpu: SpinLock::new(CpuId::this()), ptrace: SpinLock::new(PTrace::new()), + utime: AtomicUsize::new(0), + stime: AtomicUsize::new(0), + last_account: AtomicUsize::new(0), }; Self { @@ -87,6 +93,7 @@ impl OwnedTask { robust_list: None, child_tid_ptr: None, t_shared: Arc::new(task), + in_syscall: false, } } @@ -105,6 +112,9 @@ impl OwnedTask { fd_table: Arc::new(SpinLock::new(FileDescriptorTable::new())), last_cpu: SpinLock::new(CpuId::this()), ptrace: SpinLock::new(PTrace::new()), + last_account: AtomicUsize::new(0), + utime: AtomicUsize::new(0), + stime: AtomicUsize::new(0), }; Self { @@ -118,6 +128,7 @@ impl OwnedTask { robust_list: None, child_tid_ptr: None, t_shared: Arc::new(task), + in_syscall: false, } } @@ -155,4 +166,13 @@ impl OwnedTask { .peek_signal(self.sig_mask) }) } + + pub fn update_accounting(&self, curr_time: Option) { + let now = curr_time.unwrap_or_else(|| now().unwrap()); + if self.in_syscall { + self.update_stime(now); + } else { + self.update_utime(now); + } + } } diff --git a/src/process/thread_group.rs b/src/process/thread_group.rs index ed140cf1..56edf9cb 100644 --- a/src/process/thread_group.rs +++ b/src/process/thread_group.rs @@ -5,7 +5,7 @@ use alloc::{ sync::{Arc, Weak}, }; use builder::ThreadGroupBuilder; -use core::sync::atomic::AtomicU64; +use core::sync::atomic::AtomicUsize; use core::{ fmt::Display, sync::atomic::{AtomicU32, Ordering}, @@ -101,8 +101,9 @@ pub struct ThreadGroup { pub pending_signals: SpinLock, pub priority: SpinLock, pub child_notifiers: ChildNotifiers, - pub utime: AtomicU64, - pub stime: AtomicU64, + pub utime: AtomicUsize, + pub stime: AtomicUsize, + pub last_account: AtomicUsize, next_tid: AtomicU32, } diff --git a/src/process/thread_group/builder.rs b/src/process/thread_group/builder.rs index 87f89e58..d177c6f7 100644 --- a/src/process/thread_group/builder.rs +++ b/src/process/thread_group/builder.rs @@ -1,4 +1,4 @@ -use core::sync::atomic::{AtomicU32, AtomicU64}; +use core::sync::atomic::{AtomicU32, AtomicUsize}; use alloc::{collections::btree_map::BTreeMap, sync::Arc}; @@ -81,8 +81,9 @@ impl ThreadGroupBuilder { pending_signals: SpinLock::new(SigSet::empty()), child_notifiers: ChildNotifiers::new(), priority: SpinLock::new(self.pri.unwrap_or(0)), - utime: AtomicU64::new(0), - stime: AtomicU64::new(0), + utime: AtomicUsize::new(0), + stime: AtomicUsize::new(0), + last_account: AtomicUsize::new(0), // Don't start from '0'. Since clone expects the parent to return // the tid and the child to return '0', if we started from '0' we // couldn't then differentiate between a child and a parent. diff --git a/src/sched/mod.rs b/src/sched/mod.rs index 2dccefc0..35f78e3a 100644 --- a/src/sched/mod.rs +++ b/src/sched/mod.rs @@ -264,6 +264,7 @@ impl SchedState { let mut needs_resched = self.force_resched; if let Some(current) = self.run_q.current_mut() { + current.update_accounting(Some(now_inst)); // If the current task is IDLE, we always want to proceed to the // scheduler core to see if a real task has arrived. if current.is_idle_task() { @@ -310,6 +311,8 @@ impl SchedState { // Update all context since the task has switched. if let Some(new_current) = self.run_q.current_mut() { ArchImpl::context_switch(new_current.t_shared.clone()); + let now = now().unwrap(); + new_current.reset_last_account(now); CUR_TASK_PTR.borrow_mut().set_current(&mut new_current.task); } }