mirror of
https://github.com/hexagonal-sun/moss-kernel.git
synced 2026-01-30 17:11:47 -05:00
process: split Task into Task (shared) and OwnedTask (local)
This commit refactors the core process representation to decouple
"Identity/Resources" from "Execution/Scheduling". Previously, a
monolithic `Task` struct wrapped in `Arc<SpinLock<>>` caused lock
contention during hot scheduling paths and conflated shared state with
CPU-local state.
The `Task` struct has been split into:
1. `Task` (Shared): Holds process-wide resources (VM, FileTable,
Credentials). Managed via `Arc` and internal fine-grained locking.
2. `OwnedTask` (Private): Holds execution state (Context, v_runtime,
signal mask). Strictly owned by a specific CPU (via the Scheduler) and
accessed lock-free.
Key changes:
* Scheduler:
chedState` now owns tasks via `Box<OwnedTask>`.
- Transitions between `run_queue` and `running_task` involve strictly
moving ownership of the Box, ensuring pointer stability.
- The EEVDF comparison logic now explicitly handles comparisons
between the queued candidates and the currently running task (which is
not in the queue).
* Current Task Access:
- `current()` now returns a `CurrentTaskGuard` which:
1. Disables preemption (preventing context switches while holding
the reference).
2. Performs a runtime borrow check (panic on double-mutable borrow).
3. Dereferences a cached Per-CPU raw pointer for O(1) access.
This commit is contained in:
@@ -21,7 +21,7 @@ use libkernel::{
|
||||
};
|
||||
use log::info;
|
||||
|
||||
const KERNEL_STACK_SZ: usize = 64 * 1024; // 32 KiB
|
||||
const KERNEL_STACK_SZ: usize = 256 * 1024; // 32 KiB
|
||||
pub const KERNEL_STACK_PG_ORDER: usize = (KERNEL_STACK_SZ / PAGE_SIZE).ilog2() as usize;
|
||||
|
||||
const KERNEL_HEAP_SZ: usize = 64 * 1024 * 1024; // 64 MiB
|
||||
|
||||
@@ -6,7 +6,7 @@ use crate::{
|
||||
arch::ArchImpl,
|
||||
interrupts::get_interrupt_root,
|
||||
ksym_pa,
|
||||
sched::{current_task, uspc_ret::dispatch_userspace_task},
|
||||
sched::{current::current_task, uspc_ret::dispatch_userspace_task},
|
||||
spawn_kernel_work,
|
||||
};
|
||||
use aarch64_cpu::registers::{CPACR_EL1, ReadWriteable, VBAR_EL1};
|
||||
@@ -144,7 +144,7 @@ extern "C" fn el1_serror_spx(state: &mut ExceptionState) {
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
extern "C" fn el0_sync(state_ptr: *mut ExceptionState) -> *const ExceptionState {
|
||||
current_task().ctx.lock_save_irq().save_user_ctx(state_ptr);
|
||||
current_task().ctx.save_user_ctx(state_ptr);
|
||||
|
||||
let state = unsafe { state_ptr.as_ref().unwrap() };
|
||||
|
||||
@@ -174,7 +174,7 @@ extern "C" fn el0_sync(state_ptr: *mut ExceptionState) -> *const ExceptionState
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
extern "C" fn el0_irq(state: *mut ExceptionState) -> *mut ExceptionState {
|
||||
current_task().ctx.lock_save_irq().save_user_ctx(state);
|
||||
current_task().ctx.save_user_ctx(state);
|
||||
|
||||
match get_interrupt_root() {
|
||||
Some(ref im) => im.handle_interrupt(),
|
||||
|
||||
@@ -69,7 +69,7 @@ use crate::{
|
||||
},
|
||||
threading::{futex::sys_futex, sys_set_robust_list, sys_set_tid_address},
|
||||
},
|
||||
sched::{current_task, sys_sched_yield},
|
||||
sched::{current::current_task, sys_sched_yield},
|
||||
};
|
||||
use alloc::boxed::Box;
|
||||
use libkernel::{
|
||||
@@ -78,10 +78,10 @@ use libkernel::{
|
||||
};
|
||||
|
||||
pub async fn handle_syscall() {
|
||||
let task = current_task();
|
||||
|
||||
let (nr, arg1, arg2, arg3, arg4, arg5, arg6) = {
|
||||
let ctx = task.ctx.lock_save_irq();
|
||||
let mut task = current_task();
|
||||
|
||||
let ctx = &mut task.ctx;
|
||||
let state = ctx.user();
|
||||
|
||||
(
|
||||
@@ -314,8 +314,8 @@ pub async fn handle_syscall() {
|
||||
}
|
||||
0x8b => {
|
||||
// Special case for sys_rt_sigreturn
|
||||
task.ctx
|
||||
.lock_save_irq()
|
||||
current_task()
|
||||
.ctx
|
||||
.put_signal_work(Box::pin(ArchImpl::do_signal_return()));
|
||||
|
||||
return;
|
||||
@@ -449,7 +449,7 @@ pub async fn handle_syscall() {
|
||||
}
|
||||
_ => panic!(
|
||||
"Unhandled syscall 0x{nr:x}, PC: 0x{:x}",
|
||||
current_task().ctx.lock_save_irq().user().elr_el1
|
||||
current_task().ctx.user().elr_el1
|
||||
),
|
||||
};
|
||||
|
||||
@@ -458,5 +458,5 @@ pub async fn handle_syscall() {
|
||||
Err(e) => kern_err_to_syscall(e),
|
||||
};
|
||||
|
||||
task.ctx.lock_save_irq().user_mut().x[0] = ret_val.cast_unsigned() as u64;
|
||||
current_task().ctx.user_mut().x[0] = ret_val.cast_unsigned() as u64;
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ use crate::{
|
||||
memory::uaccess::UAccessResult,
|
||||
},
|
||||
memory::fault::{FaultResolution, handle_demand_fault, handle_protection_fault},
|
||||
sched::{current_task, spawn_kernel_work},
|
||||
sched::{current::current_task, spawn_kernel_work},
|
||||
};
|
||||
use alloc::boxed::Box;
|
||||
use libkernel::{
|
||||
@@ -111,7 +111,7 @@ pub fn handle_mem_fault(exception: Exception, info: AbortIss) {
|
||||
"SIGSEGV on process {} {:?} PC: {:x}",
|
||||
current_task().process.tgid,
|
||||
exception,
|
||||
current_task().ctx.lock_save_irq().user().elr_el1
|
||||
current_task().ctx.user().elr_el1
|
||||
),
|
||||
// If the page fault involves sleepy kernel work, we can
|
||||
// spawn that work on the process, since there is no other
|
||||
|
||||
@@ -21,6 +21,7 @@ use memory::{
|
||||
use crate::{
|
||||
process::{
|
||||
Task,
|
||||
owned::OwnedTask,
|
||||
thread_group::signal::{SigId, ksigaction::UserspaceSigAction},
|
||||
},
|
||||
sync::SpinLock,
|
||||
@@ -37,6 +38,7 @@ mod proc;
|
||||
pub mod psci;
|
||||
|
||||
pub struct Aarch64 {}
|
||||
|
||||
impl CpuOps for Aarch64 {
|
||||
fn id() -> usize {
|
||||
MPIDR_EL1.read(MPIDR_EL1::Aff0) as _
|
||||
@@ -109,7 +111,7 @@ impl Arch for Aarch64 {
|
||||
proc::context_switch(new);
|
||||
}
|
||||
|
||||
fn create_idle_task() -> Task {
|
||||
fn create_idle_task() -> OwnedTask {
|
||||
proc::idle::create_idle_task()
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::{
|
||||
arch::{ArchImpl, arm64::exceptions::ExceptionState},
|
||||
memory::{PageOffsetTranslator, page::ClaimedPage},
|
||||
process::Task,
|
||||
process::owned::OwnedTask,
|
||||
};
|
||||
use core::arch::global_asm;
|
||||
use libkernel::{
|
||||
@@ -16,7 +16,7 @@ use libkernel::{
|
||||
|
||||
global_asm!(include_str!("idle.s"));
|
||||
|
||||
pub fn create_idle_task() -> Task {
|
||||
pub fn create_idle_task() -> OwnedTask {
|
||||
let code_page = ClaimedPage::alloc_zeroed().unwrap().leak();
|
||||
let code_addr = VA::from_value(0xd00d0000);
|
||||
|
||||
@@ -60,5 +60,5 @@ pub fn create_idle_task() -> Task {
|
||||
VMAPermissions::rx(),
|
||||
);
|
||||
|
||||
Task::create_idle_task(addr_space, ctx, code_map)
|
||||
OwnedTask::create_idle_task(addr_space, ctx, code_map)
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ use crate::{
|
||||
process::thread_group::signal::{
|
||||
SigId, ksigaction::UserspaceSigAction, sigaction::SigActionFlags,
|
||||
},
|
||||
sched::current_task,
|
||||
sched::current::current_task,
|
||||
};
|
||||
use libkernel::{
|
||||
error::Result,
|
||||
@@ -29,7 +29,7 @@ pub async fn do_signal(id: SigId, sa: UserspaceSigAction) -> Result<ExceptionSta
|
||||
let task = current_task();
|
||||
let mut signal = task.process.signals.lock_save_irq();
|
||||
|
||||
let saved_state = *task.ctx.lock_save_irq().user();
|
||||
let saved_state = *task.ctx.user();
|
||||
let mut new_state = saved_state;
|
||||
let mut frame = RtSigFrame {
|
||||
uctx: saved_state,
|
||||
@@ -65,8 +65,7 @@ pub async fn do_signal(id: SigId, sa: UserspaceSigAction) -> Result<ExceptionSta
|
||||
pub async fn do_signal_return() -> Result<ExceptionState> {
|
||||
let task = current_task();
|
||||
|
||||
let sig_frame_addr: TUA<RtSigFrame> =
|
||||
TUA::from_value(task.ctx.lock_save_irq().user().sp_el0 as _);
|
||||
let sig_frame_addr: TUA<RtSigFrame> = TUA::from_value(task.ctx.user().sp_el0 as _);
|
||||
|
||||
let sig_frame = copy_from_user(sig_frame_addr).await?;
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
use crate::process::{
|
||||
Task,
|
||||
owned::OwnedTask,
|
||||
thread_group::signal::{SigId, ksigaction::UserspaceSigAction},
|
||||
};
|
||||
use alloc::sync::Arc;
|
||||
@@ -39,7 +40,7 @@ pub trait Arch: CpuOps + VirtualMemory {
|
||||
fn context_switch(new: Arc<Task>);
|
||||
|
||||
/// Construct a new idle task.
|
||||
fn create_idle_task() -> Task;
|
||||
fn create_idle_task() -> OwnedTask;
|
||||
|
||||
/// Powers off the machine. Implementations must never return.
|
||||
fn power_off() -> !;
|
||||
|
||||
@@ -6,7 +6,7 @@ use crate::{
|
||||
fs::open_file::OpenFile,
|
||||
kernel_driver,
|
||||
process::fd_table::Fd,
|
||||
sched::current_task,
|
||||
sched::current::current_task,
|
||||
};
|
||||
use alloc::{string::ToString, sync::Arc};
|
||||
use libkernel::{
|
||||
|
||||
@@ -3,7 +3,7 @@ use crate::{
|
||||
kernel::kpipe::KPipe,
|
||||
memory::uaccess::{copy_from_user, copy_from_user_slice, copy_to_user},
|
||||
process::thread_group::Pgid,
|
||||
sched::current_task,
|
||||
sched::current::current_task,
|
||||
sync::SpinLock,
|
||||
};
|
||||
use alloc::{boxed::Box, sync::Arc};
|
||||
|
||||
@@ -6,7 +6,7 @@ use crate::kernel::kpipe::KPipe;
|
||||
use crate::process::thread_group::Pgid;
|
||||
use crate::process::thread_group::signal::SigId;
|
||||
use crate::process::thread_group::signal::kill::send_signal_to_pg;
|
||||
use crate::sched::current_task;
|
||||
use crate::sched::current::current_task;
|
||||
use crate::sync::{CondVar, SpinLock};
|
||||
use alloc::{sync::Arc, vec::Vec};
|
||||
use libkernel::error::Result;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
use crate::process::find_task_by_descriptor;
|
||||
use crate::process::thread_group::Tgid;
|
||||
use crate::sched::current_task;
|
||||
use crate::sched::current::current_task;
|
||||
use crate::sync::OnceLock;
|
||||
use crate::{
|
||||
drivers::{Driver, FilesystemDriver},
|
||||
|
||||
@@ -263,20 +263,6 @@ pub fn schedule_preempt(when: Instant) {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn schedule_force_preempt() {
|
||||
// Schedule a preemption event if none are scheduled
|
||||
let when = now().unwrap() + Duration::from_millis(5);
|
||||
|
||||
if let Some(next_event) = WAKEUP_Q.borrow().peek()
|
||||
&& next_event.when <= when
|
||||
{
|
||||
// An event is already scheduled before our forced preemption
|
||||
return;
|
||||
}
|
||||
|
||||
schedule_preempt(when);
|
||||
}
|
||||
|
||||
static SYS_TIMER: OnceLock<Arc<SysTimer>> = OnceLock::new();
|
||||
|
||||
per_cpu! {
|
||||
|
||||
@@ -9,7 +9,9 @@ use libkernel::{
|
||||
};
|
||||
use ringbuf::Arc;
|
||||
|
||||
use crate::{memory::uaccess::copy_to_user_slice, process::fd_table::Fd, sched::current_task};
|
||||
use crate::{
|
||||
memory::uaccess::copy_to_user_slice, process::fd_table::Fd, sched::current::current_task_shared,
|
||||
};
|
||||
|
||||
use super::{fops::FileOps, open_file::FileCtx};
|
||||
|
||||
@@ -132,7 +134,7 @@ struct Dirent64Hdr {
|
||||
}
|
||||
|
||||
pub async fn sys_getdents64(fd: Fd, mut ubuf: UA, size: u32) -> Result<usize> {
|
||||
let task = current_task();
|
||||
let task = current_task_shared();
|
||||
let file = task
|
||||
.fd_table
|
||||
.lock_save_irq()
|
||||
|
||||
@@ -1,22 +1,23 @@
|
||||
use alloc::borrow::ToOwned;
|
||||
use alloc::boxed::Box;
|
||||
use alloc::{collections::btree_map::BTreeMap, sync::Arc};
|
||||
use crate::{
|
||||
drivers::{DM, Driver},
|
||||
process::Task,
|
||||
sync::SpinLock,
|
||||
};
|
||||
use alloc::{borrow::ToOwned, boxed::Box, collections::btree_map::BTreeMap, sync::Arc, vec::Vec};
|
||||
use async_trait::async_trait;
|
||||
use core::sync::atomic::{AtomicU64, Ordering};
|
||||
use dir::DirFile;
|
||||
use libkernel::error::{FsError, KernelError, Result};
|
||||
use libkernel::fs::attr::FilePermissions;
|
||||
use libkernel::fs::path::Path;
|
||||
use libkernel::fs::{BlockDevice, FS_ID_START, FileType, Filesystem, Inode, InodeId, OpenFlags};
|
||||
use libkernel::proc::caps::CapabilitiesFlags;
|
||||
use libkernel::{
|
||||
error::{FsError, KernelError, Result},
|
||||
fs::{
|
||||
BlockDevice, FS_ID_START, FileType, Filesystem, Inode, InodeId, OpenFlags,
|
||||
attr::FilePermissions, path::Path,
|
||||
},
|
||||
proc::caps::CapabilitiesFlags,
|
||||
};
|
||||
use open_file::OpenFile;
|
||||
use reg::RegFile;
|
||||
|
||||
use crate::drivers::{DM, Driver};
|
||||
use crate::process::Task;
|
||||
use crate::sync::SpinLock;
|
||||
use alloc::vec::Vec;
|
||||
|
||||
pub mod dir;
|
||||
pub mod fops;
|
||||
pub mod open_file;
|
||||
@@ -182,7 +183,7 @@ impl VFS {
|
||||
&self,
|
||||
path: &Path,
|
||||
root: Arc<dyn Inode>,
|
||||
task: Arc<Task>,
|
||||
task: &Arc<Task>,
|
||||
) -> Result<Arc<dyn Inode>> {
|
||||
let root = if path.is_absolute() {
|
||||
task.root.lock_save_irq().0.clone() // use the task's root inode, in case a custom chroot was set
|
||||
@@ -199,7 +200,7 @@ impl VFS {
|
||||
&self,
|
||||
path: &Path,
|
||||
root: Arc<dyn Inode>,
|
||||
task: Arc<Task>,
|
||||
task: &Arc<Task>,
|
||||
) -> Result<Arc<dyn Inode>> {
|
||||
let root = if path.is_absolute() {
|
||||
task.root.lock_save_irq().0.clone()
|
||||
@@ -306,10 +307,10 @@ impl VFS {
|
||||
flags: OpenFlags,
|
||||
root: Arc<dyn Inode>,
|
||||
mode: FilePermissions,
|
||||
task: Arc<Task>,
|
||||
task: &Arc<Task>,
|
||||
) -> Result<Arc<OpenFile>> {
|
||||
// Attempt to resolve the full path first.
|
||||
let resolve_result = self.resolve_path(path, root.clone(), task.clone()).await;
|
||||
let resolve_result = self.resolve_path(path, root.clone(), task).await;
|
||||
|
||||
let target_inode = match resolve_result {
|
||||
// The file/directory exists.
|
||||
@@ -413,10 +414,10 @@ impl VFS {
|
||||
path: &Path,
|
||||
root: Arc<dyn Inode>,
|
||||
mode: FilePermissions,
|
||||
task: Arc<Task>,
|
||||
task: &Arc<Task>,
|
||||
) -> Result<()> {
|
||||
// Try to resolve the target directory first.
|
||||
match self.resolve_path(path, root.clone(), task.clone()).await {
|
||||
match self.resolve_path(path, root.clone(), task).await {
|
||||
// The path already exists, this is an error.
|
||||
Ok(_) => Err(FsError::AlreadyExists.into()),
|
||||
|
||||
@@ -457,12 +458,10 @@ impl VFS {
|
||||
path: &Path,
|
||||
root: Arc<dyn Inode>,
|
||||
remove_dir: bool,
|
||||
task: Arc<Task>,
|
||||
task: &Arc<Task>,
|
||||
) -> Result<()> {
|
||||
// First, resolve the target inode so we can inspect its type.
|
||||
let target_inode = self
|
||||
.resolve_path_nofollow(path, root.clone(), task.clone())
|
||||
.await?;
|
||||
let target_inode = self.resolve_path_nofollow(path, root.clone(), task).await?;
|
||||
|
||||
let attr = target_inode.getattr().await?;
|
||||
|
||||
@@ -480,8 +479,7 @@ impl VFS {
|
||||
|
||||
// Determine the parent directory inode in which to perform the unlink.
|
||||
let parent_inode = if let Some(parent_path) = path.parent() {
|
||||
self.resolve_path(parent_path, root.clone(), task.clone())
|
||||
.await?
|
||||
self.resolve_path(parent_path, root.clone(), task).await?
|
||||
} else {
|
||||
root.clone()
|
||||
};
|
||||
@@ -527,9 +525,9 @@ impl VFS {
|
||||
target: &Path,
|
||||
link: &Path,
|
||||
root: Arc<dyn Inode>,
|
||||
task: Arc<Task>,
|
||||
task: &Arc<Task>,
|
||||
) -> Result<()> {
|
||||
match self.resolve_path(link, root.clone(), task.clone()).await {
|
||||
match self.resolve_path(link, root.clone(), task).await {
|
||||
Ok(_) => Err(FsError::AlreadyExists.into()),
|
||||
Err(KernelError::Fs(FsError::NotFound)) => {
|
||||
let name = link.file_name().ok_or(FsError::InvalidInput)?;
|
||||
|
||||
@@ -2,7 +2,7 @@ use crate::{
|
||||
kernel::kpipe::KPipe,
|
||||
memory::uaccess::copy_to_user,
|
||||
process::{fd_table::Fd, thread_group::signal::SigId},
|
||||
sched::current_task,
|
||||
sched::current::current_task,
|
||||
sync::CondVar,
|
||||
};
|
||||
use alloc::{boxed::Box, sync::Arc};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use super::{AtFlags, resolve_at_start_node};
|
||||
use crate::{
|
||||
fs::syscalls::at::resolve_path_flags, memory::uaccess::cstr::UserCStr, process::fd_table::Fd,
|
||||
sched::current_task,
|
||||
sched::current::current_task_shared,
|
||||
};
|
||||
use core::ffi::c_char;
|
||||
use libkernel::{
|
||||
@@ -17,12 +17,12 @@ pub async fn sys_faccessat(dirfd: Fd, path: TUA<c_char>, mode: i32) -> Result<us
|
||||
pub async fn sys_faccessat2(dirfd: Fd, path: TUA<c_char>, mode: i32, flags: i32) -> Result<usize> {
|
||||
let mut buf = [0; 1024];
|
||||
|
||||
let task = current_task();
|
||||
let task = current_task_shared();
|
||||
let access_mode = AccessMode::from_bits_retain(mode);
|
||||
let path = Path::new(UserCStr::from_ptr(path).copy_from_user(&mut buf).await?);
|
||||
let at_flags = AtFlags::from_bits_retain(flags);
|
||||
let start_node = resolve_at_start_node(dirfd, path, at_flags).await?;
|
||||
let node = resolve_path_flags(dirfd, path, start_node, task.clone(), at_flags).await?;
|
||||
let node = resolve_path_flags(dirfd, path, start_node, &task, at_flags).await?;
|
||||
|
||||
// If mode is F_OK (value 0), the check is for the file's existence.
|
||||
// Reaching this point means we found the file, so we can return success.
|
||||
|
||||
@@ -12,7 +12,7 @@ use crate::{
|
||||
fs::syscalls::at::{AtFlags, resolve_at_start_node, resolve_path_flags},
|
||||
memory::uaccess::cstr::UserCStr,
|
||||
process::{Task, fd_table::Fd},
|
||||
sched::current_task,
|
||||
sched::current::current_task_shared,
|
||||
};
|
||||
|
||||
pub fn can_chmod(task: Arc<Task>, uid: Uid) -> bool {
|
||||
@@ -25,12 +25,12 @@ pub async fn sys_fchmodat(dirfd: Fd, path: TUA<c_char>, mode: u16, flags: i32) -
|
||||
|
||||
let mut buf = [0; 1024];
|
||||
|
||||
let task = current_task();
|
||||
let task = current_task_shared();
|
||||
let path = Path::new(UserCStr::from_ptr(path).copy_from_user(&mut buf).await?);
|
||||
let start_node = resolve_at_start_node(dirfd, path, flags).await?;
|
||||
let mode = FilePermissions::from_bits_retain(mode);
|
||||
|
||||
let node = resolve_path_flags(dirfd, path, start_node, task.clone(), flags).await?;
|
||||
let node = resolve_path_flags(dirfd, path, start_node, &task, flags).await?;
|
||||
let mut attr = node.getattr().await?;
|
||||
|
||||
if !can_chmod(task, attr.uid) {
|
||||
|
||||
@@ -14,7 +14,7 @@ use crate::{
|
||||
fs::syscalls::at::{AtFlags, resolve_at_start_node, resolve_path_flags},
|
||||
memory::uaccess::cstr::UserCStr,
|
||||
process::fd_table::Fd,
|
||||
sched::current_task,
|
||||
sched::current::current_task_shared,
|
||||
};
|
||||
|
||||
pub async fn sys_fchownat(
|
||||
@@ -26,12 +26,12 @@ pub async fn sys_fchownat(
|
||||
) -> Result<usize> {
|
||||
let mut buf = [0; 1024];
|
||||
|
||||
let task = current_task();
|
||||
let task = current_task_shared();
|
||||
let flags = AtFlags::from_bits_retain(flags);
|
||||
let path = Path::new(UserCStr::from_ptr(path).copy_from_user(&mut buf).await?);
|
||||
let start_node = resolve_at_start_node(dirfd, path, flags).await?;
|
||||
|
||||
let node = resolve_path_flags(dirfd, path, start_node, task.clone(), flags).await?;
|
||||
let node = resolve_path_flags(dirfd, path, start_node, &task, flags).await?;
|
||||
let mut attr = node.getattr().await?;
|
||||
|
||||
{
|
||||
|
||||
@@ -14,7 +14,7 @@ use crate::{
|
||||
},
|
||||
memory::uaccess::cstr::UserCStr,
|
||||
process::fd_table::Fd,
|
||||
sched::current_task,
|
||||
sched::current::current_task_shared,
|
||||
};
|
||||
|
||||
pub async fn sys_linkat(
|
||||
@@ -27,7 +27,7 @@ pub async fn sys_linkat(
|
||||
let mut buf = [0; 1024];
|
||||
let mut buf2 = [0; 1024];
|
||||
|
||||
let task = current_task();
|
||||
let task = current_task_shared();
|
||||
let mut flags = AtFlags::from_bits_retain(flags);
|
||||
|
||||
// following symlinks is implied for any other syscall.
|
||||
@@ -60,14 +60,8 @@ pub async fn sys_linkat(
|
||||
let old_start_node = resolve_at_start_node(old_dirfd, old_path, flags).await?;
|
||||
let new_start_node = resolve_at_start_node(new_dirfd, new_path, flags).await?;
|
||||
|
||||
let target_inode = resolve_path_flags(
|
||||
old_dirfd,
|
||||
old_path,
|
||||
old_start_node.clone(),
|
||||
task.clone(),
|
||||
flags,
|
||||
)
|
||||
.await?;
|
||||
let target_inode =
|
||||
resolve_path_flags(old_dirfd, old_path, old_start_node.clone(), &task, flags).await?;
|
||||
|
||||
let attr = target_inode.getattr().await?;
|
||||
|
||||
@@ -77,7 +71,7 @@ pub async fn sys_linkat(
|
||||
|
||||
// newpath does not follow flags, and doesnt follow symlinks either
|
||||
if VFS
|
||||
.resolve_path_nofollow(new_path, new_start_node.clone(), task.clone())
|
||||
.resolve_path_nofollow(new_path, new_start_node.clone(), &task)
|
||||
.await
|
||||
.is_ok()
|
||||
{
|
||||
@@ -86,7 +80,7 @@ pub async fn sys_linkat(
|
||||
|
||||
// parent newpath should follow symlinks though
|
||||
let parent_inode = if let Some(parent) = new_path.parent() {
|
||||
VFS.resolve_path(parent, new_start_node, task).await?
|
||||
VFS.resolve_path(parent, new_start_node, &task).await?
|
||||
} else {
|
||||
new_start_node
|
||||
};
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use crate::current_task;
|
||||
use crate::fs::VFS;
|
||||
use crate::fs::syscalls::at::{AtFlags, resolve_at_start_node};
|
||||
use crate::memory::uaccess::cstr::UserCStr;
|
||||
use crate::process::fd_table::Fd;
|
||||
use crate::sched::current::current_task_shared;
|
||||
use core::ffi::c_char;
|
||||
use libkernel::fs::attr::FilePermissions;
|
||||
use libkernel::fs::path::Path;
|
||||
@@ -15,11 +15,11 @@ pub async fn sys_mkdirat(
|
||||
) -> libkernel::error::Result<usize> {
|
||||
let mut buf = [0; 1024];
|
||||
|
||||
let task = current_task();
|
||||
let task = current_task_shared();
|
||||
let path = Path::new(UserCStr::from_ptr(path).copy_from_user(&mut buf).await?);
|
||||
let start_node = resolve_at_start_node(dirfd, path, AtFlags::empty()).await?;
|
||||
let mode = FilePermissions::from_bits_retain(mode);
|
||||
|
||||
VFS.mkdir(path, start_node, mode, task.clone()).await?;
|
||||
VFS.mkdir(path, start_node, mode, &task).await?;
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::{
|
||||
fs::{DummyInode, VFS},
|
||||
process::{Task, fd_table::Fd},
|
||||
sched::current_task,
|
||||
sched::current::current_task_shared,
|
||||
};
|
||||
use alloc::sync::Arc;
|
||||
use libkernel::{
|
||||
@@ -41,7 +41,7 @@ async fn resolve_at_start_node(dirfd: Fd, path: &Path, flags: AtFlags) -> Result
|
||||
// just return a dummy, since it'll operate on dirfd anyways
|
||||
return Ok(Arc::new(DummyInode {}));
|
||||
}
|
||||
let task = current_task();
|
||||
let task = current_task_shared();
|
||||
|
||||
let start_node: Arc<dyn Inode> = if path.is_absolute() {
|
||||
// Absolute path ignores dirfd.
|
||||
@@ -73,7 +73,7 @@ async fn resolve_path_flags(
|
||||
dirfd: Fd,
|
||||
path: &Path,
|
||||
root: Arc<dyn Inode>,
|
||||
task: Arc<Task>,
|
||||
task: &Arc<Task>,
|
||||
flags: AtFlags,
|
||||
) -> Result<Arc<dyn Inode>> {
|
||||
// simply return the inode that dirfd refers to
|
||||
|
||||
@@ -2,7 +2,7 @@ use crate::{
|
||||
fs::{VFS, syscalls::at::AtFlags},
|
||||
memory::uaccess::cstr::UserCStr,
|
||||
process::fd_table::Fd,
|
||||
sched::current_task,
|
||||
sched::current::current_task_shared,
|
||||
};
|
||||
use core::ffi::c_char;
|
||||
use libkernel::{
|
||||
@@ -16,15 +16,13 @@ use super::resolve_at_start_node;
|
||||
pub async fn sys_openat(dirfd: Fd, path: TUA<c_char>, flags: u32, mode: u16) -> Result<usize> {
|
||||
let mut buf = [0; 1024];
|
||||
|
||||
let task = current_task();
|
||||
let task = current_task_shared();
|
||||
let flags = OpenFlags::from_bits_truncate(flags);
|
||||
let path = Path::new(UserCStr::from_ptr(path).copy_from_user(&mut buf).await?);
|
||||
let start_node = resolve_at_start_node(dirfd, path, AtFlags::empty()).await?;
|
||||
let mode = FilePermissions::from_bits_retain(mode);
|
||||
|
||||
let file = VFS
|
||||
.open(path, flags, start_node, mode, task.clone())
|
||||
.await?;
|
||||
let file = VFS.open(path, flags, start_node, mode, &task).await?;
|
||||
|
||||
let fd = task.fd_table.lock_save_irq().insert(file)?;
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ use crate::{
|
||||
},
|
||||
memory::uaccess::{copy_to_user_slice, cstr::UserCStr},
|
||||
process::fd_table::Fd,
|
||||
sched::current_task,
|
||||
sched::current::current_task_shared,
|
||||
};
|
||||
use core::{cmp::min, ffi::c_char};
|
||||
use libkernel::{
|
||||
@@ -17,7 +17,7 @@ use libkernel::{
|
||||
pub async fn sys_readlinkat(dirfd: Fd, path: TUA<c_char>, buf: UA, size: usize) -> Result<usize> {
|
||||
let mut path_buf = [0; 1024];
|
||||
|
||||
let task = current_task();
|
||||
let task = current_task_shared();
|
||||
let path = Path::new(
|
||||
UserCStr::from_ptr(path)
|
||||
.copy_from_user(&mut path_buf)
|
||||
@@ -28,8 +28,7 @@ pub async fn sys_readlinkat(dirfd: Fd, path: TUA<c_char>, buf: UA, size: usize)
|
||||
let name = path.file_name().ok_or(FsError::InvalidInput)?;
|
||||
|
||||
let parent = if let Some(p) = path.parent() {
|
||||
VFS.resolve_path_nofollow(p, start.clone(), task.clone())
|
||||
.await?
|
||||
VFS.resolve_path_nofollow(p, start.clone(), &task).await?
|
||||
} else {
|
||||
start
|
||||
};
|
||||
|
||||
@@ -14,7 +14,7 @@ use crate::{
|
||||
},
|
||||
memory::uaccess::cstr::UserCStr,
|
||||
process::fd_table::Fd,
|
||||
sched::current_task,
|
||||
sched::current::current_task_shared,
|
||||
};
|
||||
|
||||
// from linux/fcntl.h
|
||||
@@ -53,7 +53,8 @@ pub async fn sys_renameat2(
|
||||
let mut buf = [0; 1024];
|
||||
let mut buf2 = [0; 1024];
|
||||
|
||||
let task = current_task();
|
||||
let task = current_task_shared();
|
||||
|
||||
let old_path = Path::new(
|
||||
UserCStr::from_ptr(old_path)
|
||||
.copy_from_user(&mut buf)
|
||||
@@ -71,14 +72,14 @@ pub async fn sys_renameat2(
|
||||
let new_start_node = resolve_at_start_node(new_dirfd, new_path, AtFlags::empty()).await?;
|
||||
|
||||
let old_parent_inode = if let Some(parent_path) = old_path.parent() {
|
||||
VFS.resolve_path(parent_path, old_start_node.clone(), task.clone())
|
||||
VFS.resolve_path(parent_path, old_start_node.clone(), &task)
|
||||
.await?
|
||||
} else {
|
||||
old_start_node.clone()
|
||||
};
|
||||
|
||||
let new_parent_inode = if let Some(parent_path) = new_path.parent() {
|
||||
VFS.resolve_path(parent_path, new_start_node.clone(), task.clone())
|
||||
VFS.resolve_path(parent_path, new_start_node.clone(), &task)
|
||||
.await?
|
||||
} else {
|
||||
new_start_node.clone()
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use crate::{
|
||||
current_task,
|
||||
fs::syscalls::at::{resolve_at_start_node, resolve_path_flags},
|
||||
memory::uaccess::{UserCopyable, copy_to_user, cstr::UserCStr},
|
||||
process::fd_table::Fd,
|
||||
sched::current::current_task_shared,
|
||||
};
|
||||
use core::ffi::c_char;
|
||||
use libkernel::{
|
||||
@@ -75,7 +75,7 @@ pub async fn sys_newfstatat(
|
||||
) -> Result<usize> {
|
||||
let mut buf = [0; 1024];
|
||||
|
||||
let task = current_task();
|
||||
let task = current_task_shared();
|
||||
let flags = AtFlags::from_bits_truncate(flags);
|
||||
let path = Path::new(UserCStr::from_ptr(path).copy_from_user(&mut buf).await?);
|
||||
|
||||
@@ -84,7 +84,7 @@ pub async fn sys_newfstatat(
|
||||
Err(err) if err != KernelError::NotSupported => panic!("{err}"),
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
let node = resolve_path_flags(dirfd, path, start_node, task.clone(), flags).await?;
|
||||
let node = resolve_path_flags(dirfd, path, start_node, &task, flags).await?;
|
||||
|
||||
let attr = node.getattr().await?;
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
use crate::{
|
||||
current_task,
|
||||
fs::{
|
||||
VFS,
|
||||
syscalls::at::{resolve_at_start_node, resolve_path_flags},
|
||||
},
|
||||
memory::uaccess::{UserCopyable, copy_to_user, cstr::UserCStr},
|
||||
process::fd_table::Fd,
|
||||
sched::current::current_task_shared,
|
||||
};
|
||||
use core::{ffi::c_char, time::Duration};
|
||||
use libkernel::{error::Result, fs::path::Path, memory::address::TUA};
|
||||
@@ -124,13 +124,13 @@ pub async fn sys_statx(
|
||||
) -> Result<usize> {
|
||||
let mut buf = [0; 1024];
|
||||
|
||||
let task = current_task();
|
||||
let task = current_task_shared();
|
||||
let flags = AtFlags::from_bits_truncate(flags);
|
||||
let mask = StatXMask::from_bits_truncate(mask);
|
||||
let path = Path::new(UserCStr::from_ptr(path).copy_from_user(&mut buf).await?);
|
||||
|
||||
let start_node = resolve_at_start_node(dirfd, path, flags).await?;
|
||||
let node = resolve_path_flags(dirfd, path, start_node, task.clone(), flags).await?;
|
||||
let node = resolve_path_flags(dirfd, path, start_node, &task, flags).await?;
|
||||
|
||||
let attr = node.getattr().await?;
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ use crate::{
|
||||
},
|
||||
memory::uaccess::cstr::UserCStr,
|
||||
process::fd_table::Fd,
|
||||
sched::current_task,
|
||||
sched::current::current_task_shared,
|
||||
};
|
||||
|
||||
pub async fn sys_symlinkat(
|
||||
@@ -20,7 +20,7 @@ pub async fn sys_symlinkat(
|
||||
let mut buf = [0; 1024];
|
||||
let mut buf2 = [0; 1024];
|
||||
|
||||
let task = current_task();
|
||||
let task = current_task_shared();
|
||||
let source = Path::new(
|
||||
UserCStr::from_ptr(old_name)
|
||||
.copy_from_user(&mut buf)
|
||||
@@ -33,7 +33,7 @@ pub async fn sys_symlinkat(
|
||||
);
|
||||
let start_node = resolve_at_start_node(new_dirfd, target, AtFlags::empty()).await?;
|
||||
|
||||
VFS.symlink(source, target, start_node, task).await?;
|
||||
VFS.symlink(source, target, start_node, &task).await?;
|
||||
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
@@ -3,13 +3,13 @@ use core::ffi::c_char;
|
||||
use libkernel::{error::Result, fs::path::Path, memory::address::TUA};
|
||||
|
||||
use crate::{
|
||||
current_task,
|
||||
fs::{
|
||||
VFS,
|
||||
syscalls::at::{AtFlags, resolve_at_start_node},
|
||||
},
|
||||
memory::uaccess::cstr::UserCStr,
|
||||
process::fd_table::Fd,
|
||||
sched::current::current_task_shared,
|
||||
};
|
||||
|
||||
// As defined in linux/fcntl.h ─ enables directory removal via unlinkat.
|
||||
@@ -25,7 +25,7 @@ pub async fn sys_unlinkat(dirfd: Fd, path: TUA<c_char>, flags: u32) -> Result<us
|
||||
let mut buf = [0u8; 1024];
|
||||
let path = Path::new(UserCStr::from_ptr(path).copy_from_user(&mut buf).await?);
|
||||
|
||||
let task = current_task();
|
||||
let task = current_task_shared();
|
||||
|
||||
// Determine the starting inode for path resolution.
|
||||
let flags = AtFlags::from_bits_retain(flags as _);
|
||||
@@ -33,8 +33,7 @@ pub async fn sys_unlinkat(dirfd: Fd, path: TUA<c_char>, flags: u32) -> Result<us
|
||||
|
||||
let remove_dir = flags.bits() as u32 & AT_REMOVEDIR != 0;
|
||||
|
||||
VFS.unlink(path, start_node, remove_dir, task.clone())
|
||||
.await?;
|
||||
VFS.unlink(path, start_node, remove_dir, &task).await?;
|
||||
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
@@ -11,12 +11,13 @@ use libkernel::{
|
||||
};
|
||||
use ringbuf::Arc;
|
||||
|
||||
use crate::process::Task;
|
||||
use crate::{
|
||||
clock::{realtime::date, timespec::TimeSpec},
|
||||
current_task,
|
||||
fs::syscalls::at::{AtFlags, resolve_at_start_node, resolve_path_flags},
|
||||
memory::uaccess::{copy_from_user, cstr::UserCStr},
|
||||
process::{Task, fd_table::Fd},
|
||||
process::fd_table::Fd,
|
||||
sched::current::current_task_shared,
|
||||
};
|
||||
|
||||
const UTIME_NOW: u64 = (1 << 30) - 1;
|
||||
@@ -28,7 +29,7 @@ pub async fn sys_utimensat(
|
||||
times: TUA<[TimeSpec; 2]>,
|
||||
flags: i32,
|
||||
) -> Result<usize> {
|
||||
let task = current_task();
|
||||
let task = current_task_shared();
|
||||
|
||||
// linux specifically uses NULL path to indicate futimens, see utimensat(2)
|
||||
let node = if path.is_null() {
|
||||
@@ -45,7 +46,7 @@ pub async fn sys_utimensat(
|
||||
let flags = AtFlags::from_bits_retain(flags);
|
||||
let start_node = resolve_at_start_node(dirfd, path, flags).await?;
|
||||
|
||||
resolve_path_flags(dirfd, path, start_node, task.clone(), flags).await?
|
||||
resolve_path_flags(dirfd, path, start_node, &task, flags).await?
|
||||
};
|
||||
|
||||
let mut attr = node.getattr().await?;
|
||||
|
||||
@@ -2,7 +2,7 @@ use crate::{
|
||||
fs::VFS,
|
||||
memory::uaccess::{copy_to_user_slice, cstr::UserCStr},
|
||||
process::fd_table::Fd,
|
||||
sched::current_task,
|
||||
sched::current::current_task_shared,
|
||||
};
|
||||
use alloc::{borrow::ToOwned, ffi::CString, string::ToString};
|
||||
use core::{ffi::c_char, str::FromStr};
|
||||
@@ -14,7 +14,7 @@ use libkernel::{
|
||||
};
|
||||
|
||||
pub async fn sys_getcwd(buf: UA, len: usize) -> Result<usize> {
|
||||
let task = current_task();
|
||||
let task = current_task_shared();
|
||||
let path = task.cwd.lock_save_irq().1.as_str().to_string();
|
||||
let cstr = CString::from_str(&path).map_err(|_| KernelError::InvalidValue)?;
|
||||
let slice = cstr.as_bytes_with_nul();
|
||||
@@ -32,11 +32,11 @@ pub async fn sys_chdir(path: TUA<c_char>) -> Result<usize> {
|
||||
let mut buf = [0; 1024];
|
||||
|
||||
let path = Path::new(UserCStr::from_ptr(path).copy_from_user(&mut buf).await?);
|
||||
let task = current_task();
|
||||
let task = current_task_shared();
|
||||
let current_path = task.cwd.lock_save_irq().0.clone();
|
||||
let new_path = task.cwd.lock_save_irq().1.join(path);
|
||||
|
||||
let node = VFS.resolve_path(path, current_path, task.clone()).await?;
|
||||
let node = VFS.resolve_path(path, current_path, &task).await?;
|
||||
|
||||
*task.cwd.lock_save_irq() = (node, new_path);
|
||||
|
||||
@@ -44,7 +44,7 @@ pub async fn sys_chdir(path: TUA<c_char>) -> Result<usize> {
|
||||
}
|
||||
|
||||
pub async fn sys_chroot(path: TUA<c_char>) -> Result<usize> {
|
||||
let task = current_task();
|
||||
let task = current_task_shared();
|
||||
task.creds
|
||||
.lock_save_irq()
|
||||
.caps()
|
||||
@@ -56,7 +56,7 @@ pub async fn sys_chroot(path: TUA<c_char>) -> Result<usize> {
|
||||
let current_path = task.root.lock_save_irq().0.clone();
|
||||
let new_path = task.root.lock_save_irq().1.join(path);
|
||||
|
||||
let node = VFS.resolve_path(path, current_path, task.clone()).await?;
|
||||
let node = VFS.resolve_path(path, current_path, &task).await?;
|
||||
|
||||
*task.root.lock_save_irq() = (node, new_path);
|
||||
|
||||
@@ -64,7 +64,7 @@ pub async fn sys_chroot(path: TUA<c_char>) -> Result<usize> {
|
||||
}
|
||||
|
||||
pub async fn sys_fchdir(fd: Fd) -> Result<usize> {
|
||||
let task = current_task();
|
||||
let task = current_task_shared();
|
||||
let file = task
|
||||
.fd_table
|
||||
.lock_save_irq()
|
||||
|
||||
@@ -4,10 +4,10 @@ use libkernel::{
|
||||
fs::attr::FilePermissions,
|
||||
};
|
||||
|
||||
use crate::{process::fd_table::Fd, sched::current_task};
|
||||
use crate::{process::fd_table::Fd, sched::current::current_task_shared};
|
||||
|
||||
pub async fn sys_fchmod(fd: Fd, mode: u16) -> Result<usize> {
|
||||
let task = current_task();
|
||||
let task = current_task_shared();
|
||||
let file = task
|
||||
.fd_table
|
||||
.lock_save_irq()
|
||||
|
||||
@@ -6,10 +6,10 @@ use libkernel::{
|
||||
},
|
||||
};
|
||||
|
||||
use crate::{process::fd_table::Fd, sched::current_task};
|
||||
use crate::{process::fd_table::Fd, sched::current::current_task_shared};
|
||||
|
||||
pub async fn sys_fchown(fd: Fd, owner: i32, group: i32) -> Result<usize> {
|
||||
let task = current_task();
|
||||
let task = current_task_shared();
|
||||
let file = task
|
||||
.fd_table
|
||||
.lock_save_irq()
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::{process::fd_table::Fd, sched::current_task};
|
||||
use crate::{process::fd_table::Fd, sched::current::current_task};
|
||||
use alloc::sync::Arc;
|
||||
use libkernel::error::{KernelError, Result};
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::{process::fd_table::Fd, sched::current_task};
|
||||
use crate::{process::fd_table::Fd, sched::current::current_task};
|
||||
use libkernel::error::{KernelError, Result};
|
||||
|
||||
pub async fn sys_ioctl(fd: Fd, request: usize, arg: usize) -> Result<usize> {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::{
|
||||
memory::uaccess::{UserCopyable, copy_obj_array_from_user},
|
||||
process::fd_table::Fd,
|
||||
sched::current_task,
|
||||
sched::current::current_task,
|
||||
};
|
||||
use libkernel::{
|
||||
error::{KernelError, Result},
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::{process::fd_table::Fd, sched::current_task};
|
||||
use crate::{process::fd_table::Fd, sched::current::current_task};
|
||||
use libkernel::{
|
||||
error::{KernelError, Result},
|
||||
memory::address::UA,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::{process::fd_table::Fd, sched::current_task};
|
||||
use crate::{process::fd_table::Fd, sched::current::current_task};
|
||||
use libkernel::{
|
||||
error::{KernelError, Result},
|
||||
fs::SeekFrom,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::{kernel::kpipe::KPipe, process::fd_table::Fd, sched::current_task};
|
||||
use crate::{kernel::kpipe::KPipe, process::fd_table::Fd, sched::current::current_task};
|
||||
use alloc::sync::Arc;
|
||||
use libkernel::{
|
||||
error::{KernelError, Result},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use super::at::stat::Stat;
|
||||
use crate::memory::uaccess::copy_to_user;
|
||||
use crate::{process::fd_table::Fd, sched::current_task};
|
||||
use crate::{process::fd_table::Fd, sched::current::current_task};
|
||||
use libkernel::error::Result;
|
||||
use libkernel::{error::KernelError, memory::address::TUA};
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use libkernel::error::{KernelError, Result};
|
||||
|
||||
use crate::{fs::VFS, process::fd_table::Fd, sched::current_task};
|
||||
use crate::{fs::VFS, process::fd_table::Fd, sched::current::current_task_shared};
|
||||
|
||||
pub async fn sys_sync() -> Result<usize> {
|
||||
VFS.sync_all().await?;
|
||||
@@ -8,7 +8,7 @@ pub async fn sys_sync() -> Result<usize> {
|
||||
}
|
||||
|
||||
pub async fn sys_syncfs(fd: Fd) -> Result<usize> {
|
||||
let task = current_task();
|
||||
let task = current_task_shared();
|
||||
|
||||
let inode = task
|
||||
.fd_table
|
||||
@@ -23,7 +23,7 @@ pub async fn sys_syncfs(fd: Fd) -> Result<usize> {
|
||||
}
|
||||
|
||||
pub async fn sys_fsync(fd: Fd) -> Result<usize> {
|
||||
let task = current_task();
|
||||
let task = current_task_shared();
|
||||
|
||||
let inode = task
|
||||
.fd_table
|
||||
@@ -38,7 +38,7 @@ pub async fn sys_fsync(fd: Fd) -> Result<usize> {
|
||||
}
|
||||
|
||||
pub async fn sys_fdatasync(fd: Fd) -> Result<usize> {
|
||||
let task = current_task();
|
||||
let task = current_task_shared();
|
||||
|
||||
let inode = task
|
||||
.fd_table
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
use core::ffi::c_char;
|
||||
|
||||
use crate::{fs::VFS, memory::uaccess::cstr::UserCStr, process::fd_table::Fd, sched::current_task};
|
||||
use crate::{
|
||||
fs::VFS,
|
||||
memory::uaccess::cstr::UserCStr,
|
||||
process::fd_table::Fd,
|
||||
sched::current::{current_task, current_task_shared},
|
||||
};
|
||||
use libkernel::{
|
||||
error::{KernelError, Result},
|
||||
fs::{OpenFlags, attr::FilePermissions, path::Path},
|
||||
@@ -10,7 +15,7 @@ use libkernel::{
|
||||
pub async fn sys_truncate(path: TUA<c_char>, new_size: usize) -> Result<usize> {
|
||||
let mut buf = [0; 1024];
|
||||
|
||||
let task = current_task();
|
||||
let task = current_task_shared();
|
||||
let path = Path::new(UserCStr::from_ptr(path).copy_from_user(&mut buf).await?);
|
||||
|
||||
let root = task.root.lock_save_irq().0.clone();
|
||||
@@ -20,7 +25,7 @@ pub async fn sys_truncate(path: TUA<c_char>, new_size: usize) -> Result<usize> {
|
||||
OpenFlags::O_WRONLY,
|
||||
root,
|
||||
FilePermissions::empty(),
|
||||
task,
|
||||
&task,
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
use super::{
|
||||
ClaimedInterrupt, InterruptConfig, InterruptDescriptor, InterruptHandler, get_interrupt_root,
|
||||
};
|
||||
use crate::process::Task;
|
||||
use crate::process::owned::OwnedTask;
|
||||
use crate::{
|
||||
arch::ArchImpl,
|
||||
drivers::Driver,
|
||||
@@ -11,6 +11,7 @@ use crate::{
|
||||
sched,
|
||||
sync::{OnceLock, SpinLock},
|
||||
};
|
||||
use alloc::boxed::Box;
|
||||
use alloc::{sync::Arc, vec::Vec};
|
||||
use libkernel::{
|
||||
CpuOps,
|
||||
@@ -18,9 +19,8 @@ use libkernel::{
|
||||
};
|
||||
use log::{info, warn};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum Message {
|
||||
PutTask(Arc<Task>),
|
||||
PutTask(Box<OwnedTask>),
|
||||
Ping(u32),
|
||||
}
|
||||
|
||||
|
||||
20
src/kernel/cpu_id.rs
Normal file
20
src/kernel/cpu_id.rs
Normal file
@@ -0,0 +1,20 @@
|
||||
use libkernel::CpuOps;
|
||||
|
||||
use crate::arch::ArchImpl;
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub struct CpuId(usize);
|
||||
|
||||
impl CpuId {
|
||||
pub fn this() -> CpuId {
|
||||
CpuId(ArchImpl::id())
|
||||
}
|
||||
|
||||
pub fn from_value(id: usize) -> Self {
|
||||
Self(id)
|
||||
}
|
||||
|
||||
pub fn value(&self) -> usize {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
pub mod cpu_id;
|
||||
pub mod kpipe;
|
||||
pub mod power;
|
||||
pub mod rand;
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
use crate::{ArchImpl, arch::Arch, sched::current_task};
|
||||
use crate::{ArchImpl, arch::Arch, sched::current::current_task_shared};
|
||||
use libkernel::{
|
||||
error::{KernelError, Result},
|
||||
proc::caps::CapabilitiesFlags,
|
||||
};
|
||||
|
||||
pub async fn sys_reboot(magic: u32, magic2: u32, op: u32, _arg: usize) -> Result<usize> {
|
||||
current_task()
|
||||
current_task_shared()
|
||||
.creds
|
||||
.lock_save_irq()
|
||||
.caps()
|
||||
|
||||
11
src/main.rs
11
src/main.rs
@@ -2,6 +2,7 @@
|
||||
#![no_main]
|
||||
#![feature(used_with_arg)]
|
||||
#![feature(likely_unlikely)]
|
||||
#![feature(box_as_ptr)]
|
||||
|
||||
use alloc::{
|
||||
boxed::Box,
|
||||
@@ -27,7 +28,9 @@ use libkernel::{
|
||||
};
|
||||
use log::{error, warn};
|
||||
use process::ctx::UserCtx;
|
||||
use sched::{current_task, sched_init, spawn_kernel_work, uspc_ret::dispatch_userspace_task};
|
||||
use sched::{
|
||||
current::current_task_shared, sched_init, spawn_kernel_work, uspc_ret::dispatch_userspace_task,
|
||||
};
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
@@ -122,7 +125,7 @@ async fn launch_init(opts: KOptions) {
|
||||
.await
|
||||
.expect("Unable to find init");
|
||||
|
||||
let task = current_task();
|
||||
let task = current_task_shared();
|
||||
|
||||
// Ensure that the exec() call applies to init.
|
||||
assert!(task.process.tgid.is_init());
|
||||
@@ -138,7 +141,7 @@ async fn launch_init(opts: KOptions) {
|
||||
OpenFlags::O_RDWR,
|
||||
VFS.root_inode(),
|
||||
FilePermissions::empty(),
|
||||
task.clone(),
|
||||
&task,
|
||||
)
|
||||
.await
|
||||
.expect("Could not open console for init process");
|
||||
@@ -158,6 +161,8 @@ async fn launch_init(opts: KOptions) {
|
||||
.expect("Could not clone FD");
|
||||
}
|
||||
|
||||
drop(task);
|
||||
|
||||
process::exec::kernel_exec(inode, vec![init.as_str().to_string()], vec![])
|
||||
.await
|
||||
.expect("Could not launch init process");
|
||||
|
||||
@@ -2,7 +2,7 @@ use core::convert::Infallible;
|
||||
|
||||
use libkernel::memory::address::VA;
|
||||
|
||||
use crate::sched::current_task;
|
||||
use crate::sched::current::current_task;
|
||||
|
||||
/// Handles the `brk` system call.
|
||||
///
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::{process::ProcVM, sched::current_task};
|
||||
use crate::{process::ProcVM, sched::current::current_task};
|
||||
use alloc::boxed::Box;
|
||||
use libkernel::{
|
||||
PageInfo, UserAddressSpace,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use core::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
||||
use crate::{process::fd_table::Fd, sched::current_task};
|
||||
use crate::{process::fd_table::Fd, sched::current::current_task};
|
||||
use libkernel::{
|
||||
error::{KernelError, Result},
|
||||
memory::{
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use crate::{
|
||||
current_task,
|
||||
memory::uaccess::{
|
||||
UserCopyable, copy_from_user, copy_obj_array_from_user, copy_objs_to_user, copy_to_user,
|
||||
},
|
||||
process::TASK_LIST,
|
||||
sched::current::current_task_shared,
|
||||
};
|
||||
use libkernel::{
|
||||
error::{KernelError, Result},
|
||||
@@ -53,7 +53,7 @@ pub async fn sys_capget(hdrp: TUA<CapUserHeader>, datap: TUA<CapUserData>) -> Re
|
||||
let mut header = copy_from_user(hdrp).await?;
|
||||
|
||||
let task = if header.pid == 0 {
|
||||
current_task()
|
||||
current_task_shared()
|
||||
} else {
|
||||
TASK_LIST
|
||||
.lock_save_irq()
|
||||
@@ -85,9 +85,9 @@ pub async fn sys_capget(hdrp: TUA<CapUserHeader>, datap: TUA<CapUserData>) -> Re
|
||||
pub async fn sys_capset(hdrp: TUA<CapUserHeader>, datap: TUA<CapUserData>) -> Result<usize> {
|
||||
let mut header = copy_from_user(hdrp).await?;
|
||||
|
||||
let caller_caps = current_task().creds.lock_save_irq().caps();
|
||||
let caller_caps = current_task_shared().creds.lock_save_irq().caps();
|
||||
let task = if header.pid == 0 {
|
||||
current_task()
|
||||
current_task_shared()
|
||||
} else {
|
||||
caller_caps.check_capable(CapabilitiesFlags::CAP_SETPCAP)?;
|
||||
TASK_LIST
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
use super::owned::OwnedTask;
|
||||
use super::{ctx::Context, thread_group::signal::SigSet};
|
||||
use crate::kernel::cpu_id::CpuId;
|
||||
use crate::memory::uaccess::copy_to_user;
|
||||
use crate::sched::CpuId;
|
||||
use crate::{
|
||||
process::{TASK_LIST, Task, TaskState},
|
||||
sched::{self, current_task},
|
||||
sched::{self, current::current_task},
|
||||
sync::SpinLock,
|
||||
};
|
||||
use alloc::boxed::Box;
|
||||
use bitflags::bitflags;
|
||||
use libkernel::memory::address::TUA;
|
||||
use libkernel::{
|
||||
@@ -55,7 +57,7 @@ pub async fn sys_clone(
|
||||
let new_task = {
|
||||
let current_task = current_task();
|
||||
|
||||
let mut user_ctx = *current_task.ctx.lock_save_irq().user();
|
||||
let mut user_ctx = *current_task.ctx.user();
|
||||
|
||||
// TODO: Make this arch indepdenant. The child returns '0' on clone.
|
||||
user_ctx.x[0] = 0;
|
||||
@@ -127,52 +129,47 @@ pub async fn sys_clone(
|
||||
|
||||
let creds = current_task.creds.lock_save_irq().clone();
|
||||
|
||||
let new_sigmask = *current_task.sig_mask.lock_save_irq();
|
||||
let new_sigmask = current_task.sig_mask;
|
||||
|
||||
Task {
|
||||
tid,
|
||||
comm: Arc::new(SpinLock::new(*current_task.comm.lock_save_irq())),
|
||||
process: tg,
|
||||
vm,
|
||||
fd_table: files,
|
||||
cwd,
|
||||
root,
|
||||
creds: SpinLock::new(creds),
|
||||
ctx: SpinLock::new(Context::from_user_ctx(user_ctx)),
|
||||
OwnedTask {
|
||||
ctx: Context::from_user_ctx(user_ctx),
|
||||
priority: current_task.priority,
|
||||
sig_mask: SpinLock::new(new_sigmask),
|
||||
pending_signals: SpinLock::new(SigSet::empty()),
|
||||
v_runtime: SpinLock::new(0),
|
||||
v_eligible: SpinLock::new(0),
|
||||
v_deadline: SpinLock::new(0),
|
||||
exec_start: SpinLock::new(None),
|
||||
deadline: SpinLock::new(*current_task.deadline.lock_save_irq()),
|
||||
state: Arc::new(SpinLock::new(TaskState::Runnable)),
|
||||
last_run: SpinLock::new(None),
|
||||
robust_list: SpinLock::new(None),
|
||||
child_tid_ptr: SpinLock::new(if !child_tidptr.is_null() {
|
||||
sig_mask: new_sigmask,
|
||||
pending_signals: SigSet::empty(),
|
||||
robust_list: None,
|
||||
child_tid_ptr: if !child_tidptr.is_null() {
|
||||
Some(child_tidptr)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
t_shared: Arc::new(Task {
|
||||
tid,
|
||||
comm: Arc::new(SpinLock::new(*current_task.comm.lock_save_irq())),
|
||||
process: tg,
|
||||
vm,
|
||||
fd_table: files,
|
||||
cwd,
|
||||
root,
|
||||
creds: SpinLock::new(creds),
|
||||
state: Arc::new(SpinLock::new(TaskState::Runnable)),
|
||||
last_cpu: SpinLock::new(CpuId::this()),
|
||||
}),
|
||||
last_cpu: SpinLock::new(CpuId::this()),
|
||||
}
|
||||
};
|
||||
|
||||
let tid = new_task.tid;
|
||||
|
||||
let task = Arc::new(new_task);
|
||||
|
||||
TASK_LIST
|
||||
.lock_save_irq()
|
||||
.insert(task.descriptor(), Arc::downgrade(&task));
|
||||
.insert(new_task.descriptor(), Arc::downgrade(&new_task.t_shared));
|
||||
|
||||
sched::insert_task_cross_cpu(task.clone());
|
||||
|
||||
task.process
|
||||
new_task
|
||||
.process
|
||||
.tasks
|
||||
.lock_save_irq()
|
||||
.insert(tid, Arc::downgrade(&task));
|
||||
.insert(tid, Arc::downgrade(&new_task.t_shared));
|
||||
|
||||
sched::insert_task_cross_cpu(Box::new(new_task));
|
||||
|
||||
// Honour CLONE_*SETTID semantics for the parent and (shared-VM) child.
|
||||
if flags.contains(CloneFlags::CLONE_PARENT_SETTID) && !parent_tidptr.is_null() {
|
||||
|
||||
@@ -2,7 +2,7 @@ use core::convert::Infallible;
|
||||
|
||||
use crate::{
|
||||
memory::uaccess::{UserCopyable, copy_to_user},
|
||||
sched::current_task,
|
||||
sched::current::current_task,
|
||||
};
|
||||
use libkernel::{
|
||||
error::Result,
|
||||
@@ -100,8 +100,7 @@ pub fn sys_gettid() -> core::result::Result<usize, Infallible> {
|
||||
}
|
||||
|
||||
pub async fn sys_getresuid(ruid: TUA<Uid>, euid: TUA<Uid>, suid: TUA<Uid>) -> Result<usize> {
|
||||
let task = current_task();
|
||||
let creds = task.creds.lock_save_irq().clone();
|
||||
let creds = current_task().creds.lock_save_irq().clone();
|
||||
|
||||
copy_to_user(ruid, creds.uid).await?;
|
||||
copy_to_user(euid, creds.euid).await?;
|
||||
@@ -111,8 +110,7 @@ pub async fn sys_getresuid(ruid: TUA<Uid>, euid: TUA<Uid>, suid: TUA<Uid>) -> Re
|
||||
}
|
||||
|
||||
pub async fn sys_getresgid(rgid: TUA<Gid>, egid: TUA<Gid>, sgid: TUA<Gid>) -> Result<usize> {
|
||||
let task = current_task();
|
||||
let creds = task.creds.lock_save_irq().clone();
|
||||
let creds = current_task().creds.lock_save_irq().clone();
|
||||
|
||||
copy_to_user(rgid, creds.gid).await?;
|
||||
copy_to_user(egid, creds.egid).await?;
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
use crate::ArchImpl;
|
||||
use crate::process::Comm;
|
||||
use crate::sched::current::current_task_shared;
|
||||
use crate::{
|
||||
arch::{Arch, ArchImpl},
|
||||
arch::Arch,
|
||||
fs::VFS,
|
||||
memory::{
|
||||
page::ClaimedPage,
|
||||
uaccess::{copy_from_user, cstr::UserCStr},
|
||||
},
|
||||
process::{TaskState, ctx::Context, thread_group::signal::SignalState},
|
||||
sched::current_task,
|
||||
process::{ctx::Context, thread_group::signal::SignalState},
|
||||
sched::current::current_task,
|
||||
};
|
||||
use alloc::{string::String, vec};
|
||||
use alloc::{string::ToString, sync::Arc, vec::Vec};
|
||||
@@ -119,13 +121,13 @@ pub async fn kernel_exec(
|
||||
|
||||
let new_comm = argv.first().map(|s| Comm::new(s.as_str()));
|
||||
|
||||
let current_task = current_task();
|
||||
let mut current_task = current_task();
|
||||
|
||||
if let Some(new_comm) = new_comm {
|
||||
*current_task.comm.lock_save_irq() = new_comm;
|
||||
}
|
||||
*current_task.ctx.lock_save_irq() = Context::from_user_ctx(user_ctx);
|
||||
*current_task.state.lock_save_irq() = TaskState::Runnable;
|
||||
|
||||
current_task.ctx = Context::from_user_ctx(user_ctx);
|
||||
*current_task.vm.lock_save_irq() = vm;
|
||||
*current_task.process.signals.lock_save_irq() = SignalState::new_default();
|
||||
|
||||
@@ -248,6 +250,7 @@ pub async fn sys_execve(
|
||||
mut usr_argv: TUA<TUA<c_char>>,
|
||||
mut usr_env: TUA<TUA<c_char>>,
|
||||
) -> Result<usize> {
|
||||
let task = current_task_shared();
|
||||
let mut buf = [0; 1024];
|
||||
let mut argv = Vec::new();
|
||||
let mut envp = Vec::new();
|
||||
@@ -276,11 +279,8 @@ pub async fn sys_execve(
|
||||
usr_env = usr_env.add_objs(1);
|
||||
}
|
||||
|
||||
let task = current_task();
|
||||
let path = Path::new(UserCStr::from_ptr(path).copy_from_user(&mut buf).await?);
|
||||
let inode = VFS
|
||||
.resolve_path(path, VFS.root_inode(), task.clone())
|
||||
.await?;
|
||||
let inode = VFS.resolve_path(path, VFS.root_inode(), &task).await?;
|
||||
|
||||
kernel_exec(inode, argv, envp).await?;
|
||||
|
||||
|
||||
@@ -3,8 +3,8 @@ use super::{
|
||||
thread_group::{ProcessState, Tgid, ThreadGroup, signal::SigId, wait::ChildState},
|
||||
threading::futex::{self, key::FutexKey},
|
||||
};
|
||||
use crate::memory::uaccess::copy_to_user;
|
||||
use crate::sched::current_task;
|
||||
use crate::sched::current::current_task;
|
||||
use crate::{memory::uaccess::copy_to_user, sched::current::current_task_shared};
|
||||
use alloc::vec::Vec;
|
||||
use libkernel::error::Result;
|
||||
use log::warn;
|
||||
@@ -102,10 +102,9 @@ pub fn sys_exit_group(exit_code: usize) -> Result<usize> {
|
||||
}
|
||||
|
||||
pub async fn sys_exit(exit_code: usize) -> Result<usize> {
|
||||
let task = current_task();
|
||||
|
||||
// Honour CLONE_CHILD_CLEARTID: clear the user TID word and futex-wake any waiters.
|
||||
let ptr = task.child_tid_ptr.lock_save_irq().take();
|
||||
let ptr = current_task().child_tid_ptr.take();
|
||||
|
||||
if let Some(ptr) = ptr {
|
||||
copy_to_user(ptr, 0u32).await?;
|
||||
|
||||
@@ -116,6 +115,7 @@ pub async fn sys_exit(exit_code: usize) -> Result<usize> {
|
||||
}
|
||||
}
|
||||
|
||||
let task = current_task_shared();
|
||||
let process = Arc::clone(&task.process);
|
||||
let mut tasks_lock = process.tasks.lock_save_irq();
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::sched::current_task;
|
||||
use crate::sched::current::current_task;
|
||||
use libkernel::{
|
||||
error::{KernelError, Result},
|
||||
fs::OpenFlags,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use bitflags::Flags;
|
||||
use libkernel::error::{KernelError, Result};
|
||||
|
||||
use crate::{process::fd_table::FdFlags, sched::current_task};
|
||||
use crate::{process::fd_table::FdFlags, sched::current::current_task_shared};
|
||||
|
||||
use super::Fd;
|
||||
|
||||
@@ -12,7 +12,7 @@ const F_GETFL: u32 = 3; // Get file status flags.
|
||||
const F_SETFL: u32 = 4; // Set file status flags.
|
||||
|
||||
pub async fn sys_fcntl(fd: Fd, op: u32, arg: usize) -> Result<usize> {
|
||||
let task = current_task();
|
||||
let task = current_task_shared();
|
||||
|
||||
match op {
|
||||
F_DUPFD => todo!(),
|
||||
|
||||
@@ -12,7 +12,7 @@ use crate::{
|
||||
UserCopyable, copy_from_user, copy_obj_array_from_user, copy_objs_to_user, copy_to_user,
|
||||
},
|
||||
process::thread_group::signal::SigSet,
|
||||
sched::current_task,
|
||||
sched::current::current_task_shared,
|
||||
};
|
||||
|
||||
use super::Fd;
|
||||
@@ -68,7 +68,7 @@ pub async fn sys_pselect6(
|
||||
timeout: TUA<TimeSpec>,
|
||||
_mask: TUA<SigSet>,
|
||||
) -> Result<usize> {
|
||||
let task = current_task();
|
||||
let task = current_task_shared();
|
||||
|
||||
let mut read_fd_set = copy_from_user(readfds).await?;
|
||||
|
||||
@@ -168,7 +168,7 @@ pub async fn sys_ppoll(
|
||||
_sigmask: TUA<SigSet>,
|
||||
_sigset_len: usize,
|
||||
) -> Result<usize> {
|
||||
let task = current_task();
|
||||
let task = current_task_shared();
|
||||
|
||||
let mut poll_fds = copy_obj_array_from_user(ufds, nfds as _).await?;
|
||||
|
||||
|
||||
@@ -1,33 +1,14 @@
|
||||
use crate::drivers::timer::Instant;
|
||||
use crate::process::threading::RobustListHead;
|
||||
use crate::sched::CpuId;
|
||||
use crate::{
|
||||
arch::{Arch, ArchImpl},
|
||||
fs::DummyInode,
|
||||
sync::SpinLock,
|
||||
};
|
||||
use crate::{arch::ArchImpl, kernel::cpu_id::CpuId, sync::SpinLock};
|
||||
use alloc::{
|
||||
collections::btree_map::BTreeMap,
|
||||
sync::{Arc, Weak},
|
||||
};
|
||||
use core::fmt::Display;
|
||||
use creds::Credentials;
|
||||
use ctx::{Context, UserCtx};
|
||||
use fd_table::FileDescriptorTable;
|
||||
use libkernel::memory::address::TUA;
|
||||
use libkernel::{VirtualMemory, fs::Inode};
|
||||
use libkernel::{
|
||||
fs::pathbuf::PathBuf,
|
||||
memory::{
|
||||
address::VA,
|
||||
proc_vm::{ProcessVM, vmarea::VMArea},
|
||||
},
|
||||
};
|
||||
use thread_group::{
|
||||
Tgid, ThreadGroup,
|
||||
builder::ThreadGroupBuilder,
|
||||
signal::{SigId, SigSet, SignalState},
|
||||
};
|
||||
use libkernel::{fs::pathbuf::PathBuf, memory::proc_vm::ProcessVM};
|
||||
use thread_group::{Tgid, ThreadGroup};
|
||||
|
||||
pub mod caps;
|
||||
pub mod clone;
|
||||
@@ -36,6 +17,7 @@ pub mod ctx;
|
||||
pub mod exec;
|
||||
pub mod exit;
|
||||
pub mod fd_table;
|
||||
pub mod owned;
|
||||
pub mod sleep;
|
||||
pub mod thread_group;
|
||||
pub mod threading;
|
||||
@@ -52,6 +34,10 @@ impl Tid {
|
||||
pub fn from_tgid(tgid: Tgid) -> Self {
|
||||
Self(tgid.0)
|
||||
}
|
||||
|
||||
fn idle_for_cpu() -> Tid {
|
||||
Self(CpuId::this().value() as _)
|
||||
}
|
||||
}
|
||||
|
||||
/// A unqiue identifier for any task in the current system.
|
||||
@@ -166,11 +152,6 @@ impl Comm {
|
||||
}
|
||||
}
|
||||
|
||||
/// Scheduler base weight to ensure tasks always have a strictly positive
|
||||
/// scheduling weight. The value is added to a task's priority to obtain its
|
||||
/// effective weight (`w_i` in EEVDF paper).
|
||||
pub const SCHED_WEIGHT_BASE: i32 = 1024;
|
||||
|
||||
pub struct Task {
|
||||
pub tid: Tid,
|
||||
pub comm: Arc<SpinLock<Comm>>,
|
||||
@@ -180,114 +161,15 @@ pub struct Task {
|
||||
pub root: Arc<SpinLock<(Arc<dyn Inode>, PathBuf)>>,
|
||||
pub creds: SpinLock<Credentials>,
|
||||
pub fd_table: Arc<SpinLock<FileDescriptorTable>>,
|
||||
pub ctx: SpinLock<Context>,
|
||||
pub sig_mask: SpinLock<SigSet>,
|
||||
pub pending_signals: SpinLock<SigSet>,
|
||||
pub v_runtime: SpinLock<u128>,
|
||||
/// Virtual time at which the task becomes eligible (v_ei).
|
||||
pub v_eligible: SpinLock<u128>,
|
||||
/// Virtual deadline (v_di) used by the EEVDF scheduler.
|
||||
pub v_deadline: SpinLock<u128>,
|
||||
pub exec_start: SpinLock<Option<Instant>>,
|
||||
pub deadline: SpinLock<Option<Instant>>,
|
||||
pub priority: i8,
|
||||
pub last_run: SpinLock<Option<Instant>>,
|
||||
pub state: Arc<SpinLock<TaskState>>,
|
||||
pub robust_list: SpinLock<Option<TUA<RobustListHead>>>,
|
||||
pub child_tid_ptr: SpinLock<Option<TUA<u32>>>,
|
||||
pub last_cpu: SpinLock<CpuId>,
|
||||
}
|
||||
|
||||
impl Task {
|
||||
pub fn create_idle_task(
|
||||
addr_space: <ArchImpl as VirtualMemory>::ProcessAddressSpace,
|
||||
user_ctx: UserCtx,
|
||||
code_map: VMArea,
|
||||
) -> Self {
|
||||
// SAFETY: The code page will have been mapped corresponding to the VMA.
|
||||
let vm = unsafe { ProcessVM::from_vma_and_address_space(code_map, addr_space) };
|
||||
|
||||
let thread_group_builder = ThreadGroupBuilder::new(Tgid::idle())
|
||||
.with_sigstate(Arc::new(SpinLock::new(SignalState::new_ignore())));
|
||||
|
||||
Self {
|
||||
tid: Tid(0),
|
||||
comm: Arc::new(SpinLock::new(Comm::new("idle"))),
|
||||
process: thread_group_builder.build(),
|
||||
state: Arc::new(SpinLock::new(TaskState::Runnable)),
|
||||
priority: i8::MIN,
|
||||
cwd: Arc::new(SpinLock::new((Arc::new(DummyInode {}), PathBuf::new()))),
|
||||
root: Arc::new(SpinLock::new((Arc::new(DummyInode {}), PathBuf::new()))),
|
||||
creds: SpinLock::new(Credentials::new_root()),
|
||||
ctx: SpinLock::new(Context::from_user_ctx(user_ctx)),
|
||||
vm: Arc::new(SpinLock::new(vm)),
|
||||
sig_mask: SpinLock::new(SigSet::empty()),
|
||||
pending_signals: SpinLock::new(SigSet::empty()),
|
||||
v_runtime: SpinLock::new(0),
|
||||
v_eligible: SpinLock::new(0),
|
||||
v_deadline: SpinLock::new(0),
|
||||
exec_start: SpinLock::new(None),
|
||||
deadline: SpinLock::new(None),
|
||||
fd_table: Arc::new(SpinLock::new(FileDescriptorTable::new())),
|
||||
last_run: SpinLock::new(None),
|
||||
robust_list: SpinLock::new(None),
|
||||
child_tid_ptr: SpinLock::new(None),
|
||||
last_cpu: SpinLock::new(CpuId::this()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_init_task() -> Self {
|
||||
Self {
|
||||
tid: Tid(1),
|
||||
comm: Arc::new(SpinLock::new(Comm::new("init"))),
|
||||
process: ThreadGroupBuilder::new(Tgid::init()).build(),
|
||||
state: Arc::new(SpinLock::new(TaskState::Runnable)),
|
||||
cwd: Arc::new(SpinLock::new((Arc::new(DummyInode {}), PathBuf::new()))),
|
||||
root: Arc::new(SpinLock::new((Arc::new(DummyInode {}), PathBuf::new()))),
|
||||
creds: SpinLock::new(Credentials::new_root()),
|
||||
vm: Arc::new(SpinLock::new(
|
||||
ProcessVM::empty().expect("Could not create init process's VM"),
|
||||
)),
|
||||
fd_table: Arc::new(SpinLock::new(FileDescriptorTable::new())),
|
||||
pending_signals: SpinLock::new(SigSet::empty()),
|
||||
v_runtime: SpinLock::new(0),
|
||||
v_eligible: SpinLock::new(0),
|
||||
v_deadline: SpinLock::new(0),
|
||||
exec_start: SpinLock::new(None),
|
||||
deadline: SpinLock::new(None),
|
||||
sig_mask: SpinLock::new(SigSet::empty()),
|
||||
priority: 0,
|
||||
ctx: SpinLock::new(Context::from_user_ctx(
|
||||
<ArchImpl as Arch>::new_user_context(VA::null(), VA::null()),
|
||||
)),
|
||||
last_run: SpinLock::new(None),
|
||||
robust_list: SpinLock::new(None),
|
||||
child_tid_ptr: SpinLock::new(None),
|
||||
last_cpu: SpinLock::new(CpuId::this()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_idle_task(&self) -> bool {
|
||||
self.process.tgid.is_idle()
|
||||
}
|
||||
|
||||
pub fn priority(&self) -> i8 {
|
||||
self.priority
|
||||
}
|
||||
|
||||
/// Compute this task's scheduling weight.
|
||||
///
|
||||
/// weight = priority + SCHED_WEIGHT_BASE
|
||||
/// The sum is clamped to a minimum of 1
|
||||
pub fn weight(&self) -> u32 {
|
||||
let w = self.priority as i32 + SCHED_WEIGHT_BASE;
|
||||
if w <= 0 { 1 } else { w as u32 }
|
||||
}
|
||||
|
||||
pub fn set_priority(&mut self, priority: i8) {
|
||||
self.priority = priority;
|
||||
}
|
||||
|
||||
pub fn pgid(&self) -> Tgid {
|
||||
self.process.tgid
|
||||
}
|
||||
@@ -301,10 +183,6 @@ impl Task {
|
||||
pub fn descriptor(&self) -> TaskDescriptor {
|
||||
TaskDescriptor::from_tgid_tid(self.process.tgid, self.tid)
|
||||
}
|
||||
|
||||
pub fn raise_task_signal(&self, signal: SigId) {
|
||||
self.pending_signals.lock_save_irq().insert(signal.into());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn find_task_by_descriptor(descriptor: &TaskDescriptor) -> Option<Arc<Task>> {
|
||||
|
||||
131
src/process/owned.rs
Normal file
131
src/process/owned.rs
Normal file
@@ -0,0 +1,131 @@
|
||||
use core::ops::Deref;
|
||||
|
||||
use super::{
|
||||
Comm, Task, TaskState, Tid,
|
||||
creds::Credentials,
|
||||
ctx::{Context, UserCtx},
|
||||
fd_table::FileDescriptorTable,
|
||||
thread_group::{
|
||||
Tgid,
|
||||
builder::ThreadGroupBuilder,
|
||||
signal::{SigId, SigSet, SignalState},
|
||||
},
|
||||
threading::RobustListHead,
|
||||
};
|
||||
use crate::{
|
||||
arch::{Arch, ArchImpl},
|
||||
fs::DummyInode,
|
||||
kernel::cpu_id::CpuId,
|
||||
sync::SpinLock,
|
||||
};
|
||||
use alloc::sync::Arc;
|
||||
use libkernel::{
|
||||
VirtualMemory,
|
||||
fs::pathbuf::PathBuf,
|
||||
memory::{
|
||||
address::{TUA, VA},
|
||||
proc_vm::{ProcessVM, vmarea::VMArea},
|
||||
},
|
||||
};
|
||||
|
||||
/// Task state which is exclusively owned by this CPU/runqueue, it is not shared
|
||||
/// between other tasks and can therefore be access lock-free.
|
||||
pub struct OwnedTask {
|
||||
pub ctx: Context,
|
||||
pub sig_mask: SigSet,
|
||||
pub pending_signals: SigSet,
|
||||
pub priority: i8,
|
||||
pub robust_list: Option<TUA<RobustListHead>>,
|
||||
pub child_tid_ptr: Option<TUA<u32>>,
|
||||
pub t_shared: Arc<Task>,
|
||||
}
|
||||
|
||||
unsafe impl Send for OwnedTask {}
|
||||
unsafe impl Sync for OwnedTask {}
|
||||
|
||||
impl Deref for OwnedTask {
|
||||
type Target = Task;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.t_shared
|
||||
}
|
||||
}
|
||||
|
||||
impl OwnedTask {
|
||||
pub fn create_idle_task(
|
||||
addr_space: <ArchImpl as VirtualMemory>::ProcessAddressSpace,
|
||||
user_ctx: UserCtx,
|
||||
code_map: VMArea,
|
||||
) -> Self {
|
||||
// SAFETY: The code page will have been mapped corresponding to the VMA.
|
||||
let vm = unsafe { ProcessVM::from_vma_and_address_space(code_map, addr_space) };
|
||||
|
||||
let thread_group_builder = ThreadGroupBuilder::new(Tgid::idle())
|
||||
.with_sigstate(Arc::new(SpinLock::new(SignalState::new_ignore())));
|
||||
|
||||
let task = Task {
|
||||
tid: Tid::idle_for_cpu(),
|
||||
comm: Arc::new(SpinLock::new(Comm::new("idle"))),
|
||||
process: thread_group_builder.build(),
|
||||
state: Arc::new(SpinLock::new(TaskState::Runnable)),
|
||||
cwd: Arc::new(SpinLock::new((Arc::new(DummyInode {}), PathBuf::new()))),
|
||||
root: Arc::new(SpinLock::new((Arc::new(DummyInode {}), PathBuf::new()))),
|
||||
creds: SpinLock::new(Credentials::new_root()),
|
||||
vm: Arc::new(SpinLock::new(vm)),
|
||||
fd_table: Arc::new(SpinLock::new(FileDescriptorTable::new())),
|
||||
last_cpu: SpinLock::new(CpuId::this()),
|
||||
};
|
||||
|
||||
Self {
|
||||
priority: i8::MIN,
|
||||
ctx: Context::from_user_ctx(user_ctx),
|
||||
sig_mask: SigSet::empty(),
|
||||
pending_signals: SigSet::empty(),
|
||||
robust_list: None,
|
||||
child_tid_ptr: None,
|
||||
t_shared: Arc::new(task),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_init_task() -> Self {
|
||||
let task = Task {
|
||||
tid: Tid(1),
|
||||
comm: Arc::new(SpinLock::new(Comm::new("init"))),
|
||||
process: ThreadGroupBuilder::new(Tgid::init()).build(),
|
||||
state: Arc::new(SpinLock::new(TaskState::Runnable)),
|
||||
cwd: Arc::new(SpinLock::new((Arc::new(DummyInode {}), PathBuf::new()))),
|
||||
root: Arc::new(SpinLock::new((Arc::new(DummyInode {}), PathBuf::new()))),
|
||||
creds: SpinLock::new(Credentials::new_root()),
|
||||
vm: Arc::new(SpinLock::new(
|
||||
ProcessVM::empty().expect("Could not create init process's VM"),
|
||||
)),
|
||||
fd_table: Arc::new(SpinLock::new(FileDescriptorTable::new())),
|
||||
last_cpu: SpinLock::new(CpuId::this()),
|
||||
};
|
||||
|
||||
Self {
|
||||
pending_signals: SigSet::empty(),
|
||||
sig_mask: SigSet::empty(),
|
||||
priority: 0,
|
||||
ctx: Context::from_user_ctx(<ArchImpl as Arch>::new_user_context(
|
||||
VA::null(),
|
||||
VA::null(),
|
||||
)),
|
||||
robust_list: None,
|
||||
child_tid_ptr: None,
|
||||
t_shared: Arc::new(task),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn priority(&self) -> i8 {
|
||||
self.priority
|
||||
}
|
||||
|
||||
pub fn set_priority(&mut self, priority: i8) {
|
||||
self.priority = priority;
|
||||
}
|
||||
|
||||
pub fn raise_task_signal(&mut self, signal: SigId) {
|
||||
self.pending_signals.insert(signal.into());
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
use libkernel::error::{KernelError, Result};
|
||||
|
||||
use crate::sched::current_task;
|
||||
use crate::sched::current::current_task;
|
||||
use core::convert::Infallible;
|
||||
|
||||
use super::{Pgid, Tgid, ThreadGroup};
|
||||
|
||||
@@ -6,7 +6,7 @@ use libkernel::{
|
||||
use crate::{
|
||||
memory::uaccess::{UserCopyable, copy_from_user, copy_to_user},
|
||||
process::thread_group::{TG_LIST, Tgid},
|
||||
sched::current_task,
|
||||
sched::current::current_task,
|
||||
};
|
||||
|
||||
use super::pid::PidT;
|
||||
|
||||
@@ -3,7 +3,7 @@ use crate::{
|
||||
Tid,
|
||||
thread_group::{Pgid, Tgid, ThreadGroup, pid::PidT},
|
||||
},
|
||||
sched::current_task,
|
||||
sched::current::current_task,
|
||||
};
|
||||
|
||||
use super::{SigId, uaccess::UserSigId};
|
||||
|
||||
@@ -3,7 +3,7 @@ use libkernel::error::{KernelError, Result};
|
||||
use libkernel::memory::address::TUA;
|
||||
|
||||
use crate::memory::uaccess::{UserCopyable, copy_from_user, copy_to_user};
|
||||
use crate::sched::current_task;
|
||||
use crate::sched::current::current_task;
|
||||
|
||||
use super::ksigaction::UserspaceSigAction;
|
||||
use super::uaccess::UserSigId;
|
||||
@@ -93,8 +93,6 @@ pub async fn sys_rt_sigaction(
|
||||
oact: TUA<UserSigAction>,
|
||||
sigsetsize: usize,
|
||||
) -> Result<usize> {
|
||||
let task = current_task();
|
||||
|
||||
if sigsetsize != size_of::<SigSet>() {
|
||||
Err(KernelError::InvalidValue)?
|
||||
}
|
||||
@@ -112,6 +110,8 @@ pub async fn sys_rt_sigaction(
|
||||
};
|
||||
|
||||
let old_action = {
|
||||
let task = current_task();
|
||||
|
||||
let sigstate = task.process.signals.lock_save_irq();
|
||||
let mut action_table = sigstate.action.lock_save_irq();
|
||||
let old_action = action_table[sig];
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
memory::uaccess::{UserCopyable, copy_from_user, copy_to_user},
|
||||
sched::current_task,
|
||||
sched::current::current_task,
|
||||
};
|
||||
use bitflags::bitflags;
|
||||
use libkernel::{
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::memory::uaccess::{copy_from_user, copy_to_user};
|
||||
use crate::sched::current_task;
|
||||
use crate::sched::current::current_task;
|
||||
use libkernel::error::{KernelError, Result};
|
||||
use libkernel::memory::address::TUA;
|
||||
|
||||
@@ -26,9 +26,8 @@ pub async fn sys_rt_sigprocmask(
|
||||
};
|
||||
|
||||
let old_sigmask = {
|
||||
let task = current_task();
|
||||
let mut sigmask = task.sig_mask.lock_save_irq();
|
||||
let old_sigmask = *sigmask;
|
||||
let mut task = current_task();
|
||||
let old_sigmask = task.sig_mask;
|
||||
|
||||
if let Some(set) = set {
|
||||
let mut new_sigmask = match how {
|
||||
@@ -41,7 +40,7 @@ pub async fn sys_rt_sigprocmask(
|
||||
// SIGSTOP and SIGKILL can never be masked.
|
||||
new_sigmask = new_sigmask.union(UNMASKABLE_SIGNALS);
|
||||
|
||||
*sigmask = new_sigmask;
|
||||
task.sig_mask = new_sigmask;
|
||||
}
|
||||
|
||||
old_sigmask
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use core::convert::Infallible;
|
||||
|
||||
use crate::sched::current_task;
|
||||
use crate::sched::current::current_task;
|
||||
|
||||
pub fn sys_umask(new_umask: u32) -> core::result::Result<usize, Infallible> {
|
||||
let task = current_task();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::clock::timespec::TimeSpec;
|
||||
use crate::memory::uaccess::copy_to_user;
|
||||
use crate::sched::current_task;
|
||||
use crate::sched::current::current_task_shared;
|
||||
use crate::sync::CondVar;
|
||||
use alloc::collections::btree_map::BTreeMap;
|
||||
use bitflags::Flags;
|
||||
@@ -137,7 +137,7 @@ pub async fn sys_wait4(
|
||||
return Err(KernelError::NotSupported);
|
||||
}
|
||||
|
||||
let task = current_task();
|
||||
let task = current_task_shared();
|
||||
|
||||
let (tgid, child_state) = task
|
||||
.process
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::sched::current_task;
|
||||
use crate::sched::current::current_task;
|
||||
use libkernel::UserAddressSpace;
|
||||
use libkernel::error::{KernelError, Result};
|
||||
use libkernel::memory::address::{TUA, VA};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use core::ffi::c_long;
|
||||
use core::mem::size_of;
|
||||
|
||||
use crate::sched::current_task;
|
||||
use crate::sched::current::current_task;
|
||||
use libkernel::{
|
||||
error::{KernelError, Result},
|
||||
memory::address::TUA,
|
||||
@@ -10,9 +10,9 @@ use libkernel::{
|
||||
pub mod futex;
|
||||
|
||||
pub fn sys_set_tid_address(tidptr: TUA<u32>) -> Result<usize> {
|
||||
let task = current_task();
|
||||
let mut task = current_task();
|
||||
|
||||
*task.child_tid_ptr.lock_save_irq() = Some(tidptr);
|
||||
task.child_tid_ptr = Some(tidptr);
|
||||
|
||||
Ok(task.tid.value() as _)
|
||||
}
|
||||
@@ -36,8 +36,8 @@ pub async fn sys_set_robust_list(head: TUA<RobustListHead>, len: usize) -> Resul
|
||||
return Err(KernelError::InvalidValue);
|
||||
}
|
||||
|
||||
let task = current_task();
|
||||
task.robust_list.lock_save_irq().replace(head);
|
||||
let mut task = current_task();
|
||||
task.robust_list.replace(head);
|
||||
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
112
src/sched/current.rs
Normal file
112
src/sched/current.rs
Normal file
@@ -0,0 +1,112 @@
|
||||
use crate::{
|
||||
per_cpu,
|
||||
process::{Task, owned::OwnedTask},
|
||||
};
|
||||
use alloc::{boxed::Box, sync::Arc};
|
||||
use core::{
|
||||
cell::Cell,
|
||||
marker::PhantomData,
|
||||
ops::{Deref, DerefMut},
|
||||
ptr,
|
||||
};
|
||||
|
||||
per_cpu! {
|
||||
pub(super) static CUR_TASK_PTR: CurrentTaskPtr = CurrentTaskPtr::new;
|
||||
}
|
||||
|
||||
pub(super) struct CurrentTaskPtr {
|
||||
pub(super) ptr: Cell<*mut OwnedTask>,
|
||||
pub(super) borrowed: Cell<bool>,
|
||||
}
|
||||
|
||||
unsafe impl Send for CurrentTaskPtr {}
|
||||
|
||||
pub struct CurrentTaskGuard<'a> {
|
||||
task: &'a mut OwnedTask,
|
||||
_marker: PhantomData<*const ()>,
|
||||
}
|
||||
|
||||
impl Deref for CurrentTaskGuard<'_> {
|
||||
type Target = OwnedTask;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.task
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for CurrentTaskGuard<'_> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
self.task
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Drop for CurrentTaskGuard<'a> {
|
||||
fn drop(&mut self) {
|
||||
CUR_TASK_PTR.borrow().borrowed.set(false);
|
||||
}
|
||||
}
|
||||
|
||||
impl CurrentTaskPtr {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
ptr: Cell::new(ptr::null_mut()),
|
||||
borrowed: Cell::new(false),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn current(&self) -> CurrentTaskGuard<'static> {
|
||||
if self.borrowed.get() {
|
||||
panic!("Double mutable borrow of current task!");
|
||||
}
|
||||
|
||||
self.borrowed.set(true);
|
||||
|
||||
unsafe {
|
||||
let ptr = self.ptr.get();
|
||||
|
||||
CurrentTaskGuard {
|
||||
task: &mut *ptr,
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn set_current(&self, task: &mut Box<OwnedTask>) {
|
||||
self.ptr.set(Box::as_mut_ptr(task));
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the CPU-local private task state
|
||||
/// (`OwnedTask`).
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the current task is already borrowed on this CPU (reentrancy bug).
|
||||
/// This usually happens if you call `current_task()` and then call a function
|
||||
/// that also calls `current_task()` without dropping the first guard.
|
||||
///
|
||||
/// # Critical Section
|
||||
///
|
||||
/// This function disables preemption. You must drop the returned guard before
|
||||
/// attempting to sleep, yield, or await.
|
||||
#[track_caller]
|
||||
pub fn current_task() -> CurrentTaskGuard<'static> {
|
||||
CUR_TASK_PTR.borrow_mut().current()
|
||||
}
|
||||
|
||||
/// Returns a shared reference to the Process Identity (`Task`).
|
||||
///
|
||||
/// Use this for accessing shared resources like:
|
||||
/// - File Descriptors
|
||||
/// - Virtual Memory (Page Tables)
|
||||
/// - Current Working Directory
|
||||
/// - Credentials / PID / Thread Group
|
||||
///
|
||||
/// # Execution Context
|
||||
///
|
||||
/// This function creates a temporary `CurrentTaskGuard` just long enough to
|
||||
/// clone the `Arc`, then drops it. It is safe to await or yield after calling
|
||||
/// this function, as it does not hold the CPU lock.
|
||||
pub fn current_task_shared() -> Arc<Task> {
|
||||
current_task().t_shared.clone()
|
||||
}
|
||||
404
src/sched/mod.rs
404
src/sched/mod.rs
@@ -1,40 +1,34 @@
|
||||
use crate::drivers::timer::{Instant, now, schedule_force_preempt, schedule_preempt};
|
||||
use crate::arch::ArchImpl;
|
||||
use crate::drivers::timer::{Instant, now};
|
||||
use crate::interrupts::cpu_messenger::{Message, message_cpu};
|
||||
use crate::kernel::cpu_id::CpuId;
|
||||
use crate::process::owned::OwnedTask;
|
||||
use crate::{
|
||||
arch::{Arch, ArchImpl},
|
||||
arch::Arch,
|
||||
per_cpu,
|
||||
process::{TASK_LIST, Task, TaskDescriptor, TaskState},
|
||||
sync::OnceLock,
|
||||
process::{TASK_LIST, TaskDescriptor, TaskState},
|
||||
};
|
||||
use alloc::{boxed::Box, collections::btree_map::BTreeMap, sync::Arc};
|
||||
use core::cmp::Ordering;
|
||||
use core::sync::atomic::AtomicUsize;
|
||||
use core::time::Duration;
|
||||
use libkernel::{CpuOps, UserAddressSpace, error::Result};
|
||||
use current::{CUR_TASK_PTR, current_task};
|
||||
use libkernel::{UserAddressSpace, error::Result};
|
||||
use log::warn;
|
||||
use runqueue::{RunQueue, SwitchResult};
|
||||
use sched_task::SchedulableTask;
|
||||
|
||||
pub mod current;
|
||||
mod runqueue;
|
||||
pub mod sched_task;
|
||||
pub mod uspc_ret;
|
||||
pub mod waker;
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub struct CpuId(usize);
|
||||
|
||||
impl CpuId {
|
||||
pub fn this() -> CpuId {
|
||||
CpuId(ArchImpl::id())
|
||||
}
|
||||
|
||||
pub fn value(&self) -> usize {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: arbitrary cap.
|
||||
per_cpu! {
|
||||
static SCHED_STATE: SchedState = SchedState::new;
|
||||
}
|
||||
|
||||
/// Default time-slice (in milliseconds) assigned to runnable tasks.
|
||||
const DEFAULT_TIME_SLICE_MS: u64 = 4;
|
||||
/// Default time-slice assigned to runnable tasks.
|
||||
const DEFAULT_TIME_SLICE: Duration = Duration::from_millis(4);
|
||||
|
||||
/// Fixed-point configuration for virtual-time accounting.
|
||||
/// We now use a 65.63 format (65 integer bits, 63 fractional bits) as
|
||||
@@ -45,6 +39,11 @@ pub const VT_ONE: u128 = 1u128 << VT_FIXED_SHIFT;
|
||||
/// Two virtual-time instants whose integer parts differ by no more than this constant are considered equal.
|
||||
pub const VCLOCK_EPSILON: u128 = VT_ONE;
|
||||
|
||||
/// Scheduler base weight to ensure tasks always have a strictly positive
|
||||
/// scheduling weight. The value is added to a task's priority to obtain its
|
||||
/// effective weight (`w_i` in EEVDF paper).
|
||||
pub const SCHED_WEIGHT_BASE: i32 = 1024;
|
||||
|
||||
/// Schedule a new task.
|
||||
///
|
||||
/// This function is the core of the kernel's scheduler. It is responsible for
|
||||
@@ -65,6 +64,7 @@ pub const VCLOCK_EPSILON: u128 = VT_ONE;
|
||||
/// Nothing, but the CPU context will be set to the next runnable task. See
|
||||
/// `userspace_return` for how this is invoked.
|
||||
fn schedule() {
|
||||
// Reentrancy Check
|
||||
if SCHED_STATE.try_borrow_mut().is_none() {
|
||||
log::warn!(
|
||||
"Scheduler reentrancy detected on CPU {}",
|
||||
@@ -72,59 +72,46 @@ fn schedule() {
|
||||
);
|
||||
return;
|
||||
}
|
||||
// Mark the current task as runnable so it's considered for scheduling in
|
||||
// the next time-slice.
|
||||
{
|
||||
let task = current_task();
|
||||
let mut task_state = task.state.lock_save_irq();
|
||||
|
||||
if *task_state == TaskState::Running {
|
||||
*task_state = TaskState::Runnable;
|
||||
}
|
||||
let mut sched = SCHED_STATE.borrow_mut();
|
||||
|
||||
// Update Clocks
|
||||
let now_inst = now().expect("System timer not initialised");
|
||||
|
||||
sched.advance_vclock(now_inst);
|
||||
|
||||
if let Some(current) = sched.run_q.current_mut() {
|
||||
current.tick(now_inst);
|
||||
}
|
||||
|
||||
let previous_task = current_task();
|
||||
let mut sched_state = SCHED_STATE.borrow_mut();
|
||||
// Select Next Task
|
||||
let next_task_desc = sched.run_q.find_next_runnable_desc(sched.vclock);
|
||||
|
||||
// Bring the virtual clock up-to-date so that eligibility tests use the
|
||||
// most recent value.
|
||||
let now_inst = now().expect("System timer not initialised");
|
||||
sched_state.advance_vclock(now_inst);
|
||||
match sched.run_q.switch_tasks(next_task_desc, now_inst) {
|
||||
SwitchResult::AlreadyRunning => {
|
||||
// Nothing to do.
|
||||
return;
|
||||
}
|
||||
SwitchResult::Blocked { old_task } => {
|
||||
// If the blocked task has finished, allow it to drop here so it's
|
||||
// resources are released.
|
||||
if !old_task.state.lock_save_irq().is_finished() {
|
||||
sched.wait_q.insert(old_task.descriptor(), old_task);
|
||||
}
|
||||
}
|
||||
// fall-thru.
|
||||
SwitchResult::Preempted => {}
|
||||
}
|
||||
|
||||
let next_task = sched_state.find_next_runnable_task();
|
||||
// if previous_task.tid != next_task.tid {
|
||||
// let runnable_tasks = sched_state
|
||||
// .run_queue
|
||||
// .values()
|
||||
// .filter(|t| *t.state.lock_save_irq() == TaskState::Runnable)
|
||||
// .count();
|
||||
// if matches!(*previous_task.state.lock_save_irq(), TaskState::Sleeping | TaskState::Finished) {
|
||||
// log::debug!(
|
||||
// "CPU {} scheduling switch due to removal from run queue: {} -> {} (runnable tasks: {runnable_tasks})",
|
||||
// CpuId::this().value(),
|
||||
// previous_task.tid.value(),
|
||||
// next_task.tid.value(),
|
||||
// );
|
||||
// } else {
|
||||
// log::debug!(
|
||||
// "CPU {} scheduling switch: {} -> {} (runnable tasks: {runnable_tasks})",
|
||||
// CpuId::this().value(),
|
||||
// previous_task.tid.value(),
|
||||
// next_task.tid.value()
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
|
||||
sched_state
|
||||
.switch_to_task(Some(previous_task), next_task.clone())
|
||||
.expect("Could not schedule next task");
|
||||
// Update all context since the task has switched.
|
||||
if let Some(new_current) = sched.run_q.current_mut() {
|
||||
ArchImpl::context_switch(new_current.t_shared.clone());
|
||||
CUR_TASK_PTR.borrow_mut().set_current(&mut new_current.task);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn spawn_kernel_work(fut: impl Future<Output = ()> + 'static + Send) {
|
||||
current_task()
|
||||
.ctx
|
||||
.lock_save_irq()
|
||||
.put_kernel_work(Box::pin(fut));
|
||||
current_task().ctx.put_kernel_work(Box::pin(fut));
|
||||
}
|
||||
|
||||
#[cfg(feature = "smp")]
|
||||
@@ -134,7 +121,7 @@ fn get_next_cpu() -> CpuId {
|
||||
let cpu_count = ArchImpl::cpu_count();
|
||||
let cpu_id = NEXT_CPU.fetch_add(1, core::sync::atomic::Ordering::Relaxed) % cpu_count;
|
||||
|
||||
CpuId(cpu_id)
|
||||
CpuId::from_value(cpu_id)
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "smp"))]
|
||||
@@ -143,12 +130,14 @@ fn get_next_cpu() -> CpuId {
|
||||
}
|
||||
|
||||
/// Insert the given task onto a CPU's run queue.
|
||||
pub fn insert_task(task: Arc<Task>) {
|
||||
SCHED_STATE.borrow_mut().add_task(task);
|
||||
pub fn insert_task(task: Box<OwnedTask>) {
|
||||
SCHED_STATE
|
||||
.borrow_mut()
|
||||
.insert_into_runq(SchedulableTask::new(task));
|
||||
}
|
||||
|
||||
#[cfg(feature = "smp")]
|
||||
pub fn insert_task_cross_cpu(task: Arc<Task>) {
|
||||
pub fn insert_task_cross_cpu(task: Box<OwnedTask>) {
|
||||
let cpu = get_next_cpu();
|
||||
if cpu == CpuId::this() {
|
||||
insert_task(task);
|
||||
@@ -158,21 +147,16 @@ pub fn insert_task_cross_cpu(task: Arc<Task>) {
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "smp"))]
|
||||
pub fn insert_task_cross_cpu(task: Arc<Task>) {
|
||||
pub fn insert_task_cross_cpu(task: Box<OwnedTask>) {
|
||||
insert_task(task);
|
||||
}
|
||||
|
||||
pub struct SchedState {
|
||||
/// Task that is currently running on this CPU (if any).
|
||||
running_task: Option<Arc<Task>>,
|
||||
// TODO: To be changed to virtual-deadline key for better performance
|
||||
// TODO: Use a red-black tree for better performance.
|
||||
pub run_queue: BTreeMap<TaskDescriptor, Arc<Task>>,
|
||||
run_q: RunQueue,
|
||||
wait_q: BTreeMap<TaskDescriptor, Box<SchedulableTask>>,
|
||||
/// Per-CPU virtual clock (fixed-point 65.63 stored in a u128).
|
||||
/// Expressed in virtual-time units as defined by the EEVDF paper.
|
||||
vclock: u128,
|
||||
/// Cached sum of weights of all tasks in the run queue (`sum w_i`).
|
||||
total_weight: u64,
|
||||
/// Real-time moment when `vclock` was last updated.
|
||||
last_update: Option<Instant>,
|
||||
}
|
||||
@@ -180,80 +164,11 @@ pub struct SchedState {
|
||||
unsafe impl Send for SchedState {}
|
||||
|
||||
impl SchedState {
|
||||
/// Inserts `task` into this CPU's run-queue and updates all EEVDF
|
||||
/// accounting information (eligible time, virtual deadline and the cached
|
||||
/// weight sum).
|
||||
pub fn add_task(&mut self, task: Arc<Task>) {
|
||||
// Always advance the virtual clock first so that eligibility and
|
||||
// deadline calculations for the incoming task are based on the most
|
||||
// recent time stamp.
|
||||
let now_inst = now().expect("System timer not initialised");
|
||||
self.advance_vclock(now_inst);
|
||||
|
||||
let desc = task.descriptor();
|
||||
|
||||
if self.run_queue.contains_key(&desc) {
|
||||
return;
|
||||
}
|
||||
|
||||
// A freshly enqueued task becomes eligible immediately.
|
||||
*task.v_eligible.lock_save_irq() = self.vclock;
|
||||
|
||||
// Grant it an initial virtual deadline proportional to its weight.
|
||||
let q_ns: u128 = (DEFAULT_TIME_SLICE_MS as u128) * 1_000_000;
|
||||
let v_delta = (q_ns << VT_FIXED_SHIFT) / task.weight() as u128;
|
||||
let new_v_deadline = self.vclock + v_delta;
|
||||
*task.v_deadline.lock_save_irq() = new_v_deadline;
|
||||
|
||||
// Since the task is not executing yet, its exec_start must be `None`.
|
||||
*task.exec_start.lock_save_irq() = None;
|
||||
|
||||
if !task.is_idle_task() {
|
||||
self.total_weight = self.total_weight.saturating_add(task.weight() as u64);
|
||||
}
|
||||
|
||||
// Decide whether the currently-running task must be preempted
|
||||
// immediately.
|
||||
let newcomer_eligible = {
|
||||
let v_e = *task.v_eligible.lock_save_irq();
|
||||
v_e.saturating_sub(self.vclock) <= VCLOCK_EPSILON
|
||||
};
|
||||
let preempt_now = if newcomer_eligible {
|
||||
if let Some(ref current) = self.running_task {
|
||||
let current_deadline = *current.v_deadline.lock_save_irq();
|
||||
new_v_deadline < current_deadline
|
||||
} else {
|
||||
true
|
||||
}
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
self.run_queue.insert(desc, task);
|
||||
|
||||
// Arm an immediate preemption timer so that the interrupt
|
||||
// handler will force the actual context switch as soon as possible.
|
||||
if preempt_now {
|
||||
schedule_preempt(now_inst + Duration::from_nanos(1));
|
||||
}
|
||||
}
|
||||
|
||||
/// Removes a task given its descriptor and subtracts its weight from the
|
||||
/// cached `total_weight`. Missing descriptors are ignored.
|
||||
pub fn remove_task_with_weight(&mut self, desc: &TaskDescriptor) {
|
||||
if let Some(task) = self.run_queue.remove(desc) {
|
||||
if task.is_idle_task() {
|
||||
panic!("Cannot remove the idle task");
|
||||
}
|
||||
self.total_weight = self.total_weight.saturating_sub(task.weight() as u64);
|
||||
}
|
||||
}
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
running_task: None,
|
||||
run_queue: BTreeMap::new(),
|
||||
run_q: RunQueue::new(),
|
||||
wait_q: BTreeMap::new(),
|
||||
vclock: 0,
|
||||
total_weight: 0,
|
||||
last_update: None,
|
||||
}
|
||||
}
|
||||
@@ -266,161 +181,35 @@ impl SchedState {
|
||||
fn advance_vclock(&mut self, now_inst: Instant) {
|
||||
if let Some(prev) = self.last_update {
|
||||
let delta_real = now_inst - prev;
|
||||
if self.total_weight > 0 {
|
||||
if self.run_q.weight() > 0 {
|
||||
let delta_vt =
|
||||
((delta_real.as_nanos()) << VT_FIXED_SHIFT) / self.total_weight as u128;
|
||||
((delta_real.as_nanos()) << VT_FIXED_SHIFT) / self.run_q.weight() as u128;
|
||||
self.vclock = self.vclock.saturating_add(delta_vt);
|
||||
}
|
||||
}
|
||||
self.last_update = Some(now_inst);
|
||||
}
|
||||
|
||||
fn switch_to_task(
|
||||
&mut self,
|
||||
previous_task: Option<Arc<Task>>,
|
||||
next_task: Arc<Task>,
|
||||
) -> Result<()> {
|
||||
let now_inst = now().expect("System timer not initialised");
|
||||
// Update the virtual clock before we do any other accounting.
|
||||
self.advance_vclock(now_inst);
|
||||
fn insert_into_runq(&mut self, task: Box<SchedulableTask>) {
|
||||
let now = now().expect("systimer not running");
|
||||
|
||||
if let Some(ref prev_task) = previous_task {
|
||||
*prev_task.last_run.lock_save_irq() = Some(now_inst);
|
||||
}
|
||||
self.advance_vclock(now);
|
||||
|
||||
if let Some(ref prev_task) = previous_task
|
||||
&& Arc::ptr_eq(&next_task, prev_task)
|
||||
{
|
||||
// Ensure the task state is running.
|
||||
*next_task.state.lock_save_irq() = TaskState::Running;
|
||||
// TODO: Fix hack
|
||||
if next_task.is_idle_task() {
|
||||
schedule_force_preempt();
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Update vruntime, clear exec_start and assign a new eligible virtual deadline
|
||||
// for the previous task.
|
||||
if let Some(ref prev_task) = previous_task {
|
||||
// Compute how much virtual time the task actually consumed.
|
||||
let delta_vt = if let Some(start) = *prev_task.exec_start.lock_save_irq() {
|
||||
let delta = now_inst - start;
|
||||
let w = prev_task.weight() as u128;
|
||||
let dv = ((delta.as_nanos() as u128) << VT_FIXED_SHIFT) / w;
|
||||
*prev_task.v_runtime.lock_save_irq() += dv;
|
||||
dv
|
||||
} else {
|
||||
0
|
||||
};
|
||||
*prev_task.exec_start.lock_save_irq() = None;
|
||||
|
||||
// Advance its eligible time by the virtual run time it just used
|
||||
// (EEVDF: v_ei += t_used / w_i).
|
||||
*prev_task.v_eligible.lock_save_irq() += delta_vt;
|
||||
|
||||
// Re-issue a virtual deadline
|
||||
let q_ns: u128 = (DEFAULT_TIME_SLICE_MS as u128) * 1_000_000;
|
||||
let v_delta = (q_ns << VT_FIXED_SHIFT) / prev_task.weight() as u128;
|
||||
let v_ei = *prev_task.v_eligible.lock_save_irq();
|
||||
*prev_task.v_deadline.lock_save_irq() = v_ei + v_delta;
|
||||
}
|
||||
|
||||
*next_task.exec_start.lock_save_irq() = Some(now_inst);
|
||||
*next_task.last_cpu.lock_save_irq() = CpuId::this();
|
||||
|
||||
// Make sure the task possesses an eligible virtual deadline. If none is set
|
||||
// (or the previous one has elapsed), we hand out a brand-new one.
|
||||
{
|
||||
let mut deadline_guard = next_task.deadline.lock_save_irq();
|
||||
// Refresh deadline if none is set or the previous deadline has elapsed.
|
||||
if deadline_guard
|
||||
.is_none_or(|d| d <= now_inst + Duration::from_millis(DEFAULT_TIME_SLICE_MS))
|
||||
{
|
||||
*deadline_guard = Some(now_inst + Duration::from_millis(DEFAULT_TIME_SLICE_MS));
|
||||
}
|
||||
if let Some(d) = *deadline_guard {
|
||||
// log::debug!(
|
||||
// "CPU {}: Next task {} has deadline in {}ms",
|
||||
// CpuId::this().value(),
|
||||
// next_task.tid.value(),
|
||||
// (d - now_inst).as_millis()
|
||||
// );
|
||||
schedule_preempt(d);
|
||||
}
|
||||
}
|
||||
|
||||
*next_task.state.lock_save_irq() = TaskState::Running;
|
||||
|
||||
// Update the scheduler's state to reflect the new running task.
|
||||
self.running_task = Some(next_task.clone());
|
||||
|
||||
// Perform the architecture-specific context switch.
|
||||
ArchImpl::context_switch(next_task);
|
||||
|
||||
Ok(())
|
||||
self.run_q.enqueue_task(task, self.vclock);
|
||||
}
|
||||
|
||||
fn find_next_runnable_task(&self) -> Arc<Task> {
|
||||
let idle_task = self
|
||||
.run_queue
|
||||
.get(&TaskDescriptor::this_cpus_idle())
|
||||
.expect("Every runqueue should have an idle task");
|
||||
|
||||
self.run_queue
|
||||
.values()
|
||||
// We only care about processes that are ready to run.
|
||||
.filter(|candidate_proc| {
|
||||
let state = *candidate_proc.state.lock_save_irq();
|
||||
let eligible_vt = *candidate_proc.v_eligible.lock_save_irq();
|
||||
state == TaskState::Runnable
|
||||
&& !candidate_proc.is_idle_task()
|
||||
// Allow a small epsilon tolerance to compensate for rounding
|
||||
&& eligible_vt.saturating_sub(self.vclock) <= VCLOCK_EPSILON
|
||||
})
|
||||
.min_by(|proc1, proc2| {
|
||||
if proc1.is_idle_task() {
|
||||
return Ordering::Greater;
|
||||
} else if proc2.is_idle_task() {
|
||||
return Ordering::Less;
|
||||
}
|
||||
let vd1 = *proc1.v_deadline.lock_save_irq();
|
||||
let vd2 = *proc2.v_deadline.lock_save_irq();
|
||||
|
||||
vd1.cmp(&vd2).then_with(|| {
|
||||
let vr1 = *proc1.v_runtime.lock_save_irq();
|
||||
let vr2 = *proc2.v_runtime.lock_save_irq();
|
||||
|
||||
vr1.cmp(&vr2).then_with(|| {
|
||||
let last_run1 = proc1.last_run.lock_save_irq();
|
||||
let last_run2 = proc2.last_run.lock_save_irq();
|
||||
|
||||
match (*last_run1, *last_run2) {
|
||||
(Some(t1), Some(t2)) => t1.cmp(&t2),
|
||||
(Some(_), None) => Ordering::Less,
|
||||
(None, Some(_)) => Ordering::Greater,
|
||||
(None, None) => Ordering::Equal,
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
.unwrap_or(idle_task)
|
||||
.clone()
|
||||
pub fn wakeup(&mut self, desc: TaskDescriptor) {
|
||||
if let Some(task) = self.wait_q.remove(&desc) {
|
||||
self.insert_into_runq(task);
|
||||
} else {
|
||||
warn!("Spurious wakeup for task {:?}", desc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn current_task() -> Arc<Task> {
|
||||
SCHED_STATE
|
||||
.borrow()
|
||||
.running_task
|
||||
.as_ref()
|
||||
.expect("Current task called before initial task created")
|
||||
.clone()
|
||||
}
|
||||
|
||||
pub fn sched_init() {
|
||||
let idle_task = get_idle_task();
|
||||
let init_task = Arc::new(Task::create_init_task());
|
||||
let idle_task = ArchImpl::create_idle_task();
|
||||
let init_task = OwnedTask::create_init_task();
|
||||
|
||||
init_task
|
||||
.vm
|
||||
@@ -429,44 +218,25 @@ pub fn sched_init() {
|
||||
.address_space_mut()
|
||||
.activate();
|
||||
|
||||
*init_task.state.lock_save_irq() = TaskState::Running;
|
||||
SCHED_STATE.borrow_mut().running_task = Some(idle_task.clone());
|
||||
*init_task.state.lock_save_irq() = TaskState::Runnable;
|
||||
|
||||
{
|
||||
let mut task_list = TASK_LIST.lock_save_irq();
|
||||
|
||||
task_list.insert(idle_task.descriptor(), Arc::downgrade(&idle_task));
|
||||
task_list.insert(init_task.descriptor(), Arc::downgrade(&init_task));
|
||||
task_list.insert(idle_task.descriptor(), Arc::downgrade(&idle_task.t_shared));
|
||||
task_list.insert(init_task.descriptor(), Arc::downgrade(&init_task.t_shared));
|
||||
}
|
||||
|
||||
insert_task(idle_task);
|
||||
insert_task(init_task.clone());
|
||||
insert_task(Box::new(idle_task));
|
||||
insert_task(Box::new(init_task));
|
||||
|
||||
SCHED_STATE
|
||||
.borrow_mut()
|
||||
.switch_to_task(None, init_task)
|
||||
.expect("Failed to switch to init task");
|
||||
schedule();
|
||||
}
|
||||
|
||||
pub fn sched_init_secondary() {
|
||||
let idle_task = get_idle_task();
|
||||
SCHED_STATE.borrow_mut().running_task = Some(idle_task.clone());
|
||||
let idle_task = ArchImpl::create_idle_task();
|
||||
|
||||
// Important to ensure that the idle task is in the TASK_LIST for this CPU.
|
||||
insert_task(idle_task.clone());
|
||||
|
||||
SCHED_STATE
|
||||
.borrow_mut()
|
||||
.switch_to_task(None, idle_task)
|
||||
.expect("Failed to switch to idle task");
|
||||
}
|
||||
|
||||
fn get_idle_task() -> Arc<Task> {
|
||||
static IDLE_TASK: OnceLock<Arc<Task>> = OnceLock::new();
|
||||
|
||||
IDLE_TASK
|
||||
.get_or_init(|| Arc::new(ArchImpl::create_idle_task()))
|
||||
.clone()
|
||||
insert_task(Box::new(idle_task));
|
||||
}
|
||||
|
||||
pub fn sys_sched_yield() -> Result<usize> {
|
||||
|
||||
176
src/sched/runqueue.rs
Normal file
176
src/sched/runqueue.rs
Normal file
@@ -0,0 +1,176 @@
|
||||
use core::cmp::Ordering;
|
||||
|
||||
use crate::{
|
||||
drivers::timer::Instant,
|
||||
process::{TaskDescriptor, TaskState},
|
||||
};
|
||||
use alloc::{boxed::Box, collections::btree_map::BTreeMap};
|
||||
use log::warn;
|
||||
|
||||
use super::{VCLOCK_EPSILON, sched_task::SchedulableTask};
|
||||
|
||||
/// The result of a requested task switch.
|
||||
pub enum SwitchResult {
|
||||
/// The requested task is already running. No changes made.
|
||||
AlreadyRunning,
|
||||
/// A switch occurred. The previous task was Runnable and has been
|
||||
/// re-queued.
|
||||
Preempted,
|
||||
/// A switch occurred. The previous task is Blocked (or Finished) and
|
||||
/// ownership is returned to the caller (to handle sleep/wait queues).
|
||||
Blocked { old_task: Box<SchedulableTask> },
|
||||
}
|
||||
|
||||
/// A simple weight-tracking runqueue.
|
||||
///
|
||||
/// Invariants:
|
||||
/// 1. `total_weight` = Sum(queue tasks) + Weight(running_task) (excluding the idle task).
|
||||
/// 2. `running_task` is NOT in `queue`.
|
||||
pub struct RunQueue {
|
||||
total_weight: u64,
|
||||
pub(super) queue: BTreeMap<TaskDescriptor, Box<SchedulableTask>>,
|
||||
pub(super) running_task: Option<Box<SchedulableTask>>,
|
||||
}
|
||||
|
||||
impl RunQueue {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
total_weight: 0,
|
||||
queue: BTreeMap::new(),
|
||||
running_task: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn insert_task(&mut self, task: Box<SchedulableTask>) {
|
||||
if !task.is_idle_task() {
|
||||
self.total_weight = self.total_weight.saturating_add(task.weight() as u64);
|
||||
}
|
||||
|
||||
if let Some(old_task) = self.queue.insert(task.descriptor(), task) {
|
||||
// Handle the edge case where we overwrite a task. If we replaced
|
||||
// someone, we must subtract their weight to avoid accounting drift.
|
||||
warn!("Overwrote active task {:?}", old_task.descriptor());
|
||||
self.total_weight = self.total_weight.saturating_sub(old_task.weight() as u64);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn switch_tasks(&mut self, next_task: TaskDescriptor, now_inst: Instant) -> SwitchResult {
|
||||
if let Some(current) = self.current() {
|
||||
if current.descriptor() == next_task {
|
||||
return SwitchResult::AlreadyRunning;
|
||||
}
|
||||
}
|
||||
|
||||
let mut new_task = match self.queue.remove(&next_task) {
|
||||
Some(t) => t,
|
||||
None => {
|
||||
warn!("Task {:?} not found for switch.", next_task);
|
||||
return SwitchResult::AlreadyRunning;
|
||||
}
|
||||
};
|
||||
|
||||
new_task.about_to_execute(now_inst);
|
||||
|
||||
// Perform the swap.
|
||||
if let Some(old_task) = self.running_task.replace(new_task) {
|
||||
let state = *old_task.state.lock_save_irq();
|
||||
|
||||
match state {
|
||||
TaskState::Running | TaskState::Runnable => {
|
||||
// Update state to strictly Runnable
|
||||
*old_task.state.lock_save_irq() = TaskState::Runnable;
|
||||
|
||||
self.queue.insert(old_task.descriptor(), old_task);
|
||||
|
||||
return SwitchResult::Preempted;
|
||||
}
|
||||
_ => {
|
||||
self.total_weight = self.total_weight.saturating_sub(old_task.weight() as u64);
|
||||
|
||||
return SwitchResult::Blocked { old_task };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If there was no previous task (e.g., boot up), it counts as a
|
||||
// Preemption.
|
||||
SwitchResult::Preempted
|
||||
}
|
||||
|
||||
pub fn weight(&self) -> u64 {
|
||||
self.total_weight
|
||||
}
|
||||
|
||||
pub fn current(&self) -> Option<&Box<SchedulableTask>> {
|
||||
self.running_task.as_ref()
|
||||
}
|
||||
|
||||
pub fn current_mut(&mut self) -> Option<&mut Box<SchedulableTask>> {
|
||||
self.running_task.as_mut()
|
||||
}
|
||||
|
||||
fn fallback_current_or_idle(&self) -> TaskDescriptor {
|
||||
if let Some(ref current) = self.running_task {
|
||||
let s = *current.state.lock_save_irq();
|
||||
if !current.is_idle_task() && (s == TaskState::Runnable || s == TaskState::Running) {
|
||||
return current.descriptor();
|
||||
}
|
||||
}
|
||||
|
||||
TaskDescriptor::this_cpus_idle()
|
||||
}
|
||||
|
||||
/// Returns the Descriptor of the best task to run next. This compares the
|
||||
/// best task in the run_queue against the currently running task.
|
||||
pub fn find_next_runnable_desc(&self, vclock: u128) -> TaskDescriptor {
|
||||
// Find the best candidate from the Run Queue
|
||||
let best_queued_entry = self
|
||||
.queue
|
||||
.iter()
|
||||
.filter(|(_, task)| {
|
||||
!task.is_idle_task() && task.v_eligible.saturating_sub(vclock) <= VCLOCK_EPSILON
|
||||
})
|
||||
.min_by(|(_, t1), (_, t2)| t1.compare_with(t2));
|
||||
|
||||
let (best_queued_desc, best_queued_task) = match best_queued_entry {
|
||||
Some((d, t)) => (*d, t),
|
||||
// If runqueue is empty (or no eligible tasks), we might just run
|
||||
// current or idle.
|
||||
None => return self.fallback_current_or_idle(),
|
||||
};
|
||||
|
||||
// Compare against the current task
|
||||
if let Some(current) = self.current() {
|
||||
// If current is not Runnable (e.g. it blocked, yielded, or
|
||||
// finished), it cannot win.
|
||||
let current_state = *current.state.lock_save_irq();
|
||||
if current_state != TaskState::Runnable && current_state != TaskState::Running {
|
||||
return best_queued_desc;
|
||||
}
|
||||
|
||||
// compare current vs challenger
|
||||
match current.compare_with(best_queued_task) {
|
||||
Ordering::Less | Ordering::Equal => {
|
||||
// Current is better (has earlier deadline) or equal. Keep
|
||||
// running current.
|
||||
return current.descriptor();
|
||||
}
|
||||
Ordering::Greater => {
|
||||
// Queued task is better. Switch.
|
||||
return best_queued_desc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
best_queued_desc
|
||||
}
|
||||
|
||||
/// Inserts `task` into this CPU's run-queue and updates all EEVDF
|
||||
/// accounting information (eligible time, virtual deadline and the cached
|
||||
/// weight sum).
|
||||
pub fn enqueue_task(&mut self, mut new_task: Box<SchedulableTask>, vclock: u128) {
|
||||
new_task.inserting_into_runqueue(vclock);
|
||||
|
||||
self.insert_task(new_task);
|
||||
}
|
||||
}
|
||||
142
src/sched/sched_task.rs
Normal file
142
src/sched/sched_task.rs
Normal file
@@ -0,0 +1,142 @@
|
||||
use core::{
|
||||
cmp::Ordering,
|
||||
ops::{Deref, DerefMut},
|
||||
};
|
||||
|
||||
use alloc::boxed::Box;
|
||||
|
||||
use crate::{
|
||||
drivers::timer::{Instant, schedule_preempt},
|
||||
kernel::cpu_id::CpuId,
|
||||
process::{TaskState, owned::OwnedTask},
|
||||
};
|
||||
|
||||
use super::{DEFAULT_TIME_SLICE, SCHED_WEIGHT_BASE, VT_FIXED_SHIFT};
|
||||
|
||||
pub struct SchedulableTask {
|
||||
pub task: Box<OwnedTask>,
|
||||
pub v_runtime: u128,
|
||||
/// Virtual time at which the task becomes eligible (v_ei).
|
||||
pub v_eligible: u128,
|
||||
/// Virtual deadline (v_di) used by the EEVDF scheduler.
|
||||
pub v_deadline: u128,
|
||||
pub exec_start: Option<Instant>,
|
||||
pub deadline: Option<Instant>,
|
||||
pub last_run: Option<Instant>,
|
||||
}
|
||||
|
||||
impl Deref for SchedulableTask {
|
||||
type Target = OwnedTask;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.task
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for SchedulableTask {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.task
|
||||
}
|
||||
}
|
||||
|
||||
impl SchedulableTask {
|
||||
pub fn new(task: Box<OwnedTask>) -> Box<Self> {
|
||||
Box::new(Self {
|
||||
task,
|
||||
v_runtime: 0,
|
||||
v_eligible: 0,
|
||||
v_deadline: 0,
|
||||
exec_start: None,
|
||||
deadline: None,
|
||||
last_run: None,
|
||||
})
|
||||
}
|
||||
|
||||
/// Update accounting info for this task given the latest time.
|
||||
pub fn tick(&mut self, now: Instant) {
|
||||
let delta_vt = if let Some(start) = self.exec_start {
|
||||
let delta = now - start;
|
||||
let w = self.weight() as u128;
|
||||
let dv = ((delta.as_nanos() as u128) << VT_FIXED_SHIFT) / w;
|
||||
self.v_runtime = dv;
|
||||
dv
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
// Advance its eligible time by the virtual run time it just used
|
||||
// (EEVDF: v_ei += t_used / w_i).
|
||||
self.v_eligible += delta_vt;
|
||||
|
||||
// Re-issue a virtual deadline
|
||||
let q_ns: u128 = DEFAULT_TIME_SLICE.as_nanos();
|
||||
let v_delta = (q_ns << VT_FIXED_SHIFT) / self.weight() as u128;
|
||||
let v_ei = self.v_eligible;
|
||||
self.v_deadline = v_ei + v_delta;
|
||||
}
|
||||
|
||||
/// Compute this task's scheduling weight.
|
||||
///
|
||||
/// weight = priority + SCHED_WEIGHT_BASE
|
||||
/// The sum is clamped to a minimum of 1
|
||||
pub fn weight(&self) -> u32 {
|
||||
let w = self.task.priority as i32 + SCHED_WEIGHT_BASE;
|
||||
if w <= 0 { 1 } else { w as u32 }
|
||||
}
|
||||
|
||||
pub fn compare_with(&self, other: &Self) -> core::cmp::Ordering {
|
||||
if self.is_idle_task() {
|
||||
return Ordering::Greater;
|
||||
}
|
||||
|
||||
if other.is_idle_task() {
|
||||
return Ordering::Less;
|
||||
}
|
||||
|
||||
self.v_deadline
|
||||
.cmp(&other.v_deadline)
|
||||
.then_with(|| self.v_runtime.cmp(&other.v_runtime))
|
||||
// If completely equal, prefer the one that hasn't run in a while?
|
||||
// Or prefer the one already running to avoid cache thrashing?
|
||||
// Usually irrelevant for EEVDF but strict ordering is good for
|
||||
// stability.
|
||||
.then_with(|| match (self.last_run, other.last_run) {
|
||||
(Some(a), Some(b)) => a.cmp(&b),
|
||||
(Some(_), None) => Ordering::Less,
|
||||
(None, Some(_)) => Ordering::Greater,
|
||||
(None, None) => Ordering::Equal,
|
||||
})
|
||||
}
|
||||
|
||||
/// Update accounting information when the task is about to be inserted into
|
||||
/// a runqueue.
|
||||
pub fn inserting_into_runqueue(&mut self, vclock: u128) {
|
||||
// A freshly enqueued task becomes eligible immediately.
|
||||
self.v_eligible = vclock;
|
||||
|
||||
// Grant it an initial virtual deadline proportional to its weight.
|
||||
let q_ns: u128 = DEFAULT_TIME_SLICE.as_nanos();
|
||||
let v_delta = (q_ns << VT_FIXED_SHIFT) / self.weight() as u128;
|
||||
let new_v_deadline = vclock + v_delta;
|
||||
self.v_deadline = new_v_deadline;
|
||||
|
||||
// Since the task is not executing yet, its exec_start must be `None`.
|
||||
self.exec_start = None;
|
||||
}
|
||||
|
||||
/// Setup task accounting info such that it is about to be executed.
|
||||
pub fn about_to_execute(&mut self, now: Instant) {
|
||||
self.exec_start = Some(now);
|
||||
*self.last_cpu.lock_save_irq() = CpuId::this();
|
||||
*self.state.lock_save_irq() = TaskState::Running;
|
||||
|
||||
// Deadline logic
|
||||
if self.deadline.is_none_or(|d| d <= now + DEFAULT_TIME_SLICE) {
|
||||
self.deadline = Some(now + DEFAULT_TIME_SLICE);
|
||||
}
|
||||
|
||||
if let Some(d) = self.deadline {
|
||||
schedule_preempt(d);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
use super::{SCHED_STATE, current_task, schedule, waker::create_waker};
|
||||
use crate::process::TASK_LIST;
|
||||
use super::{current::current_task, schedule, waker::create_waker};
|
||||
use crate::{
|
||||
arch::{Arch, ArchImpl},
|
||||
process::{
|
||||
@@ -83,21 +82,26 @@ pub fn dispatch_userspace_task(ctx: *mut UserCtx) {
|
||||
state = State::ProcessKernelWork;
|
||||
}
|
||||
State::ProcessKernelWork => {
|
||||
let task = current_task();
|
||||
|
||||
// First, let's handle signals. If there is any scheduled signal
|
||||
// work (this has to be async to handle faults, etc).
|
||||
let signal_work = task.ctx.lock_save_irq().take_signal_work();
|
||||
let (signal_work, desc, is_idle) = {
|
||||
let mut task = current_task();
|
||||
(
|
||||
task.ctx.take_signal_work(),
|
||||
task.descriptor(),
|
||||
task.is_idle_task(),
|
||||
)
|
||||
};
|
||||
|
||||
if let Some(mut signal_work) = signal_work {
|
||||
if task.is_idle_task() {
|
||||
if is_idle {
|
||||
panic!("Signal processing for idle task");
|
||||
}
|
||||
|
||||
match signal_work
|
||||
.as_mut()
|
||||
.poll(&mut core::task::Context::from_waker(&create_waker(
|
||||
task.descriptor(),
|
||||
))) {
|
||||
.poll(&mut core::task::Context::from_waker(&create_waker(desc)))
|
||||
{
|
||||
Poll::Ready(Ok(state)) => {
|
||||
// Signal actioning is complete. Return to userspace.
|
||||
unsafe { ptr::copy_nonoverlapping(&state as _, ctx, 1) };
|
||||
@@ -114,7 +118,9 @@ pub fn dispatch_userspace_task(ctx: *mut UserCtx) {
|
||||
continue;
|
||||
}
|
||||
Poll::Pending => {
|
||||
task.ctx.lock_save_irq().put_signal_work(signal_work);
|
||||
let mut task = current_task();
|
||||
|
||||
task.ctx.put_signal_work(signal_work);
|
||||
let mut task_state = task.state.lock_save_irq();
|
||||
|
||||
match *task_state {
|
||||
@@ -145,29 +151,24 @@ pub fn dispatch_userspace_task(ctx: *mut UserCtx) {
|
||||
}
|
||||
|
||||
// Now let's handle any kernel work that's been spawned for this task.
|
||||
let kern_work = task.ctx.lock_save_irq().take_kernel_work();
|
||||
let kern_work = current_task().ctx.take_kernel_work();
|
||||
if let Some(mut kern_work) = kern_work {
|
||||
if task.is_idle_task() {
|
||||
if is_idle {
|
||||
panic!("Idle process should never have kernel work");
|
||||
}
|
||||
|
||||
match kern_work
|
||||
.as_mut()
|
||||
.poll(&mut core::task::Context::from_waker(&create_waker(
|
||||
task.descriptor(),
|
||||
))) {
|
||||
.poll(&mut core::task::Context::from_waker(&create_waker(desc)))
|
||||
{
|
||||
Poll::Ready(()) => {
|
||||
// If the task just exited (entered the finished state),
|
||||
// don't return to it's userspace, instead, find another
|
||||
// task to execute, removing this task from the
|
||||
// runqueue, reaping it's resouces.
|
||||
if task.state.lock_save_irq().is_finished() {
|
||||
SCHED_STATE
|
||||
.borrow_mut()
|
||||
.remove_task_with_weight(&task.descriptor());
|
||||
let mut task_list = TASK_LIST.lock_save_irq();
|
||||
task_list.remove(&task.descriptor());
|
||||
let task = current_task();
|
||||
|
||||
// If the task just exited (entered the finished
|
||||
// state), don't return to it's userspace, instead,
|
||||
// find another task to execute, removing this task
|
||||
// from the runqueue, reaping it's resouces.
|
||||
if task.state.lock_save_irq().is_finished() {
|
||||
state = State::PickNewTask;
|
||||
continue;
|
||||
}
|
||||
@@ -180,12 +181,14 @@ pub fn dispatch_userspace_task(ctx: *mut UserCtx) {
|
||||
continue;
|
||||
}
|
||||
Poll::Pending => {
|
||||
let mut task = current_task();
|
||||
|
||||
// Kernel work hasn't finished. A wake up should
|
||||
// have been scheduled by the future. Replace the
|
||||
// kernel work context back into the task, set it's
|
||||
// state to sleeping so it's not scheduled again and
|
||||
// search for another task to execute.
|
||||
task.ctx.lock_save_irq().put_kernel_work(kern_work);
|
||||
task.ctx.put_kernel_work(kern_work);
|
||||
let mut task_state = task.state.lock_save_irq();
|
||||
|
||||
match *task_state {
|
||||
@@ -215,21 +218,22 @@ pub fn dispatch_userspace_task(ctx: *mut UserCtx) {
|
||||
// No kernel work. Check for any pending signals.
|
||||
|
||||
// We never handle signals for the idle task.
|
||||
if task.is_idle_task() {
|
||||
if current_task().is_idle_task() {
|
||||
state = State::ReturnToUserspace;
|
||||
continue;
|
||||
}
|
||||
|
||||
// See if there are any signals we need to action.
|
||||
let task = current_task();
|
||||
let mut pending_task_sigs = task.pending_signals.lock_save_irq();
|
||||
let mask = task.sig_mask.lock_save_irq();
|
||||
if let Some((id, action)) = task
|
||||
.process
|
||||
.signals
|
||||
.lock_save_irq()
|
||||
.action_signal(*mask, &mut pending_task_sigs)
|
||||
{
|
||||
if let Some((id, action)) = {
|
||||
let task = current_task();
|
||||
let mut pending_task_sigs = task.pending_signals;
|
||||
|
||||
let mask = task.sig_mask;
|
||||
task.process
|
||||
.signals
|
||||
.lock_save_irq()
|
||||
.action_signal(mask, &mut pending_task_sigs)
|
||||
} {
|
||||
match action {
|
||||
KSignalAction::Term | KSignalAction::Core => {
|
||||
// Terminate the process, and find a new task.
|
||||
@@ -239,6 +243,8 @@ pub fn dispatch_userspace_task(ctx: *mut UserCtx) {
|
||||
continue;
|
||||
}
|
||||
KSignalAction::Stop => {
|
||||
let task = current_task();
|
||||
|
||||
// Default action: stop (suspend) the entire process.
|
||||
let process = &task.process;
|
||||
|
||||
@@ -265,6 +271,7 @@ pub fn dispatch_userspace_task(ctx: *mut UserCtx) {
|
||||
continue;
|
||||
}
|
||||
KSignalAction::Continue => {
|
||||
let task = current_task();
|
||||
let process = &task.process;
|
||||
|
||||
// Wake up all sleeping threads in the process.
|
||||
@@ -297,7 +304,7 @@ pub fn dispatch_userspace_task(ctx: *mut UserCtx) {
|
||||
KSignalAction::Userspace(id, action) => {
|
||||
let fut = ArchImpl::do_signal(id, action);
|
||||
|
||||
task.ctx.lock_save_irq().put_signal_work(Box::pin(fut));
|
||||
current_task().ctx.put_signal_work(Box::pin(fut));
|
||||
|
||||
state = State::ProcessKernelWork;
|
||||
continue;
|
||||
@@ -310,7 +317,7 @@ pub fn dispatch_userspace_task(ctx: *mut UserCtx) {
|
||||
|
||||
State::ReturnToUserspace => {
|
||||
// Real user-space return now.
|
||||
current_task().ctx.lock_save_irq().restore_user_ctx(ctx);
|
||||
current_task().ctx.restore_user_ctx(ctx);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
use crate::process::{TASK_LIST, TaskDescriptor, TaskState};
|
||||
use core::task::{RawWaker, RawWakerVTable, Waker};
|
||||
|
||||
use super::SCHED_STATE;
|
||||
|
||||
unsafe fn clone_waker(data: *const ()) -> RawWaker {
|
||||
RawWaker::new(data, &VTABLE)
|
||||
}
|
||||
@@ -17,6 +19,8 @@ unsafe fn wake_waker(data: *const ()) {
|
||||
// If the task has been put to sleep, then wake it up.
|
||||
TaskState::Sleeping => {
|
||||
*state = TaskState::Runnable;
|
||||
|
||||
SCHED_STATE.borrow_mut().wakeup(desc);
|
||||
}
|
||||
// If the task is running, mark it so it doesn't actually go to
|
||||
// sleep when poll returns. This covers the small race-window
|
||||
|
||||
Reference in New Issue
Block a user