Files
moss-kernel/src/process/mod.rs
2026-04-18 18:39:28 -07:00

363 lines
11 KiB
Rust

use crate::drivers::timer::Instant;
use crate::sched::CPU_STAT;
use crate::sched::sched_task::Work;
use crate::{
arch::ArchImpl,
kernel::cpu_id::CpuId,
memory::{
PAGE_ALLOC,
fault::{FaultResolution, handle_demand_fault},
},
sync::SpinLock,
};
use alloc::{
boxed::Box,
collections::btree_map::BTreeMap,
sync::{Arc, Weak},
};
use core::sync::atomic::{AtomicU32, AtomicUsize, Ordering};
use core::time::Duration;
use creds::Credentials;
use fd_table::FileDescriptorTable;
use libkernel::memory::proc_vm::address_space::{UserAddressSpace, VirtualMemory};
use libkernel::{
error::{KernelError, Result},
fs::{Inode, pathbuf::PathBuf},
memory::{
address::{UA, VA},
allocators::phys::PageAllocation,
proc_vm::{ProcessVM, vmarea::AccessKind},
},
sync::waker_set::WakerSet,
};
use ptrace::PTrace;
use thread_group::pid::PidT;
use thread_group::signal::{AtomicSigSet, SigId};
use thread_group::{Tgid, ThreadGroup};
pub mod caps;
pub mod clone;
pub mod creds;
pub mod ctx;
pub mod epoll;
pub mod exec;
pub mod exit;
pub mod fd_table;
pub mod owned;
pub mod pidfd;
pub mod prctl;
pub mod ptrace;
pub mod sleep;
pub mod thread_group;
pub mod threading;
// the idle process (0) and the init process (1) are allocated manually.
static NEXT_TID: AtomicU32 = AtomicU32::new(2);
// Thread Id.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct Tid(pub u32);
impl Tid {
pub fn value(self) -> u32 {
self.0
}
pub fn from_tgid(tgid: Tgid) -> Self {
Self(tgid.0)
}
fn idle_for_cpu() -> Tid {
Self(CpuId::this().value() as _)
}
pub fn next_tid() -> Self {
Self(NEXT_TID.fetch_add(1, Ordering::Relaxed))
}
pub fn from_pid_t(pid: PidT) -> Self {
Self(pid as _)
}
}
/// A unqiue identifier for any task in the current system.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct TaskDescriptor {
tid: Tid,
tgid: Tgid,
}
impl TaskDescriptor {
pub fn from_tgid_tid(tgid: Tgid, tid: Tid) -> Self {
Self { tid, tgid }
}
/// Returns a descriptor for the idle task.
pub fn this_cpus_idle() -> Self {
Self {
tgid: Tgid(0),
tid: Tid(CpuId::this().value() as _),
}
}
/// Returns a representation of a descriptor encoded in a single pointer
/// value.
#[cfg(target_pointer_width = "64")]
pub fn to_ptr(self) -> *const () {
let mut value: u64 = self.tgid.value() as _;
value |= (self.tid.value() as u64) << 32;
value as _
}
/// Returns a descriptor decoded from a single pointer value. This is the
/// inverse of `to_ptr`.
#[cfg(target_pointer_width = "64")]
pub fn from_ptr(ptr: *const ()) -> Self {
let value = ptr as u64;
let tgid = value & 0xffffffff;
let tid = value >> 32;
Self {
tgid: Tgid(tgid as _),
tid: Tid(tid as _),
}
}
pub fn is_idle(&self) -> bool {
self.tgid.is_idle()
}
/// Returns the task-group ID (i.e. the PID) associated with this descriptor.
pub fn tgid(&self) -> Tgid {
self.tgid
}
/// Returns the thread ID associated with this descriptor.
pub fn tid(&self) -> Tid {
self.tid
}
}
pub type ProcVM = ProcessVM<<ArchImpl as VirtualMemory>::ProcessAddressSpace>;
#[derive(Copy, Clone)]
pub struct Comm([u8; 16]);
impl Comm {
/// Create a new command name from the given string.
/// Truncates to 15 characters if necessary, and null-terminates.
pub fn new(name: &str) -> Self {
let mut comm = [0u8; 16];
let bytes = name.as_bytes();
let len = core::cmp::min(bytes.len(), 15);
comm[..len].copy_from_slice(&bytes[..len]);
Self(comm)
}
pub fn as_str(&self) -> &str {
let len = self.0.iter().position(|&c| c == 0).unwrap_or(16);
core::str::from_utf8(&self.0[..len]).unwrap_or("")
}
}
#[derive(Copy, Clone, Debug)]
pub struct ITimer {
/// If interval is `None`, this timer is a one-shot timer.
pub interval: Option<Duration>,
/// Instant (wrt the needed clock) at which this timer will next expire.
pub next: Instant,
}
#[derive(Copy, Clone, Default)]
pub struct ITimers {
pub real: Option<ITimer>,
// virtual is a reserved keyword
pub virtual_: Option<ITimer>,
pub prof: Option<ITimer>,
}
pub struct Task {
pub tid: Tid,
pub comm: Arc<SpinLock<Comm>>,
pub process: Arc<ThreadGroup>,
pub vm: Arc<SpinLock<ProcVM>>,
pub cwd: Arc<SpinLock<(Arc<dyn Inode>, PathBuf)>>,
pub root: Arc<SpinLock<(Arc<dyn Inode>, PathBuf)>>,
pub creds: SpinLock<Credentials>,
pub i_timers: SpinLock<ITimers>,
pub fd_table: Arc<SpinLock<FileDescriptorTable>>,
pub ptrace: SpinLock<PTrace>,
pub sig_mask: AtomicSigSet,
pub pending_signals: AtomicSigSet,
pub signal_notifier: SpinLock<WakerSet>,
pub utime: AtomicUsize,
pub stime: AtomicUsize,
pub last_account: AtomicUsize,
}
impl Task {
pub fn is_idle_task(&self) -> bool {
self.process.tgid.is_idle()
}
pub fn pgid(&self) -> Tgid {
self.process.tgid
}
pub fn tid(&self) -> Tid {
self.tid
}
/// Raise a signal on this specific task (thread-directed).
pub fn raise_task_signal(&self, signal: SigId) {
self.pending_signals.insert(signal.into());
self.notify_signal_waiters();
}
pub fn notify_signal_waiters(&self) {
self.signal_notifier.lock_save_irq().wake_all();
}
/// Check for a pending signal on this task or its process, respecting the
/// signal mask.
pub fn peek_signal(&self) -> Option<SigId> {
let mask = self.sig_mask.load();
self.pending_signals.peek_signal(mask).or_else(|| {
self.process
.pending_signals
.lock_save_irq()
.peek_signal(mask)
})
}
/// Take a pending signal from this task or its process, respecting the
/// signal mask.
pub fn take_signal(&self) -> Option<SigId> {
let mask = self.sig_mask.load();
self.pending_signals.take_signal(mask).or_else(|| {
self.process
.pending_signals
.lock_save_irq()
.take_signal(mask)
})
}
/// Return a new descriptor that uniquely represents this task in the
/// system.
pub fn descriptor(&self) -> TaskDescriptor {
TaskDescriptor::from_tgid_tid(self.process.tgid, self.tid)
}
/// Get a page from the task's address space, in an atomic fashion - i.e.
/// with the process address space locked.
///
/// Handle any faults such that the page will be resident in memory and return
/// an incremented refcount for the page such that it will not be free'd until
/// the returned allocation handle is dropped.
///
/// SAFETY: The caller *must* guarantee that the returned page will only be
/// used as described in `access_kind`. i.e. if `AccessKind::Read` is passed
/// but data is written to this page, *bad* things will happen.
pub async unsafe fn get_page(
&self,
va: UA,
access_kind: AccessKind,
) -> Result<PageAllocation<'static, ArchImpl>> {
let va = VA::from_value(va.value());
let mut fut = None;
loop {
if let Some(fut) = fut.take() {
// Handle async fault.
Box::into_pin(fut).await?;
}
{
let mut vm = self.vm.lock_save_irq();
if let Some(pa) = vm.mm_mut().address_space_mut().translate(va) {
let region = pa.pfn.as_phys_range();
if match access_kind {
AccessKind::Read => pa.perms.is_read(),
AccessKind::Write => pa.perms.is_write(),
AccessKind::Execute => pa.perms.is_execute(),
} {
let alloc = unsafe { PAGE_ALLOC.get().unwrap().alloc_from_region(region) };
// Increase refcount on this page, ensuring it isn't reused
// while we copy the data.
let ret = alloc.clone();
// The original allocation is still owned by the address
// space.
alloc.leak();
return Ok(ret);
}
}
}
// Try to handle the fault.
match handle_demand_fault(self.vm.clone(), va, access_kind)? {
// Resolved the fault. Try again
FaultResolution::Resolved => continue,
FaultResolution::Denied => return Err(KernelError::Fault),
FaultResolution::Deferred(future) => {
fut = Some(future);
continue;
}
}
}
}
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(Ordering::Relaxed);
let delta = now.saturating_sub(last_account);
if self.is_idle_task() {
CPU_STAT.get().idle.fetch_add(delta, Ordering::Relaxed);
} else {
CPU_STAT.get().user.fetch_add(delta, Ordering::Relaxed);
}
self.utime.fetch_add(delta, Ordering::Relaxed);
self.process.utime.fetch_add(delta, Ordering::Relaxed);
self.last_account.store(now, 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(Ordering::Relaxed);
let delta = now.saturating_sub(last_account);
CPU_STAT.get().system.fetch_add(delta, Ordering::Relaxed);
self.stime.fetch_add(delta, Ordering::Relaxed);
self.process.stime.fetch_add(delta, Ordering::Relaxed);
self.last_account.store(now, 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, Ordering::Relaxed);
self.last_account.store(now, Ordering::Relaxed);
}
}
/// Finds a task by it's `Tid`.
pub fn find_task_by_tid(tid: Tid) -> Option<Arc<Work>> {
TASK_LIST
.lock_save_irq()
.get(&tid)
.and_then(|x| x.upgrade())
}
pub static TASK_LIST: SpinLock<BTreeMap<Tid, Weak<Work>>> = SpinLock::new(BTreeMap::new());
unsafe impl Send for Task {}
unsafe impl Sync for Task {}