2024-01-03 03:22:36 +00:00
|
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
|
|
2022-08-08 01:01:42 +00:00
|
|
|
//! Tasks are the unit of code execution.
|
|
|
|
|
|
2024-08-22 13:17:38 +00:00
|
|
|
pub(crate) mod atomic_mode;
|
2024-08-22 11:23:33 +00:00
|
|
|
mod kernel_stack;
|
2024-08-15 11:36:46 +00:00
|
|
|
mod preempt;
|
2022-08-23 09:50:07 +00:00
|
|
|
mod processor;
|
2024-07-27 10:38:14 +00:00
|
|
|
pub mod scheduler;
|
2022-08-08 01:01:42 +00:00
|
|
|
|
2024-08-22 11:23:33 +00:00
|
|
|
use core::{any::Any, cell::UnsafeCell};
|
|
|
|
|
|
|
|
|
|
use kernel_stack::KernelStack;
|
2024-08-26 08:12:31 +00:00
|
|
|
pub(crate) use preempt::cpu_local::reset_preempt_info;
|
2024-08-22 11:23:33 +00:00
|
|
|
use processor::current_task;
|
2024-08-26 08:12:31 +00:00
|
|
|
|
2024-02-25 14:09:24 +00:00
|
|
|
pub use self::{
|
2024-08-19 12:32:14 +00:00
|
|
|
preempt::{disable_preempt, DisabledPreemptGuard},
|
2024-08-22 11:23:33 +00:00
|
|
|
scheduler::info::{AtomicCpuId, Priority, TaskScheduleInfo},
|
2024-02-25 14:09:24 +00:00
|
|
|
};
|
2024-08-22 11:23:33 +00:00
|
|
|
pub(crate) use crate::arch::task::{context_switch, TaskContext};
|
|
|
|
|
use crate::{cpu::CpuSet, prelude::*, user::UserSpace};
|
|
|
|
|
|
|
|
|
|
/// A task that executes a function to the end.
|
|
|
|
|
///
|
|
|
|
|
/// Each task is associated with per-task data and an optional user space.
|
|
|
|
|
/// If having a user space, the task can switch to the user space to
|
|
|
|
|
/// execute user code. Multiple tasks can share a single user space.
|
|
|
|
|
pub struct Task {
|
|
|
|
|
func: Box<dyn Fn() + Send + Sync>,
|
|
|
|
|
data: Box<dyn Any + Send + Sync>,
|
|
|
|
|
user_space: Option<Arc<UserSpace>>,
|
|
|
|
|
ctx: UnsafeCell<TaskContext>,
|
|
|
|
|
/// kernel stack, note that the top is SyscallFrame/TrapFrame
|
|
|
|
|
kstack: KernelStack,
|
|
|
|
|
|
|
|
|
|
schedule_info: TaskScheduleInfo,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SAFETY: `UnsafeCell<TaskContext>` is not `Sync`. However, we only use it in `schedule()` where
|
|
|
|
|
// we have exclusive access to the field.
|
|
|
|
|
unsafe impl Sync for Task {}
|
|
|
|
|
|
|
|
|
|
impl Task {
|
|
|
|
|
/// Gets the current task.
|
|
|
|
|
///
|
|
|
|
|
/// It returns `None` if the function is called in the bootstrap context.
|
|
|
|
|
pub fn current() -> Option<Arc<Task>> {
|
|
|
|
|
current_task()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub(super) fn ctx(&self) -> &UnsafeCell<TaskContext> {
|
|
|
|
|
&self.ctx
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Yields execution so that another task may be scheduled.
|
|
|
|
|
///
|
|
|
|
|
/// Note that this method cannot be simply named "yield" as the name is
|
|
|
|
|
/// a Rust keyword.
|
|
|
|
|
pub fn yield_now() {
|
|
|
|
|
scheduler::yield_now()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Runs the task.
|
|
|
|
|
///
|
|
|
|
|
/// BUG: This method highly depends on the current scheduling policy.
|
|
|
|
|
pub fn run(self: &Arc<Self>) {
|
|
|
|
|
scheduler::run_new_task(self.clone());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Returns the task data.
|
|
|
|
|
pub fn data(&self) -> &Box<dyn Any + Send + Sync> {
|
|
|
|
|
&self.data
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Get the attached scheduling information.
|
|
|
|
|
pub fn schedule_info(&self) -> &TaskScheduleInfo {
|
|
|
|
|
&self.schedule_info
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Returns the user space of this task, if it has.
|
|
|
|
|
pub fn user_space(&self) -> Option<&Arc<UserSpace>> {
|
|
|
|
|
if self.user_space.is_some() {
|
|
|
|
|
Some(self.user_space.as_ref().unwrap())
|
|
|
|
|
} else {
|
|
|
|
|
None
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Exits the current task.
|
|
|
|
|
///
|
|
|
|
|
/// The task `self` must be the task that is currently running.
|
|
|
|
|
///
|
|
|
|
|
/// **NOTE:** If there is anything left on the stack, it will be forgotten. This behavior may
|
|
|
|
|
/// lead to resource leakage.
|
|
|
|
|
fn exit(self: Arc<Self>) -> ! {
|
|
|
|
|
// `current_task()` still holds a strong reference, so nothing is destroyed at this point,
|
|
|
|
|
// neither is the kernel stack.
|
|
|
|
|
drop(self);
|
|
|
|
|
scheduler::exit_current();
|
|
|
|
|
unreachable!()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Options to create or spawn a new task.
|
|
|
|
|
pub struct TaskOptions {
|
|
|
|
|
func: Option<Box<dyn Fn() + Send + Sync>>,
|
|
|
|
|
data: Option<Box<dyn Any + Send + Sync>>,
|
|
|
|
|
user_space: Option<Arc<UserSpace>>,
|
|
|
|
|
priority: Priority,
|
|
|
|
|
cpu_affinity: CpuSet,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl TaskOptions {
|
|
|
|
|
/// Creates a set of options for a task.
|
|
|
|
|
pub fn new<F>(func: F) -> Self
|
|
|
|
|
where
|
|
|
|
|
F: Fn() + Send + Sync + 'static,
|
|
|
|
|
{
|
|
|
|
|
Self {
|
|
|
|
|
func: Some(Box::new(func)),
|
|
|
|
|
data: None,
|
|
|
|
|
user_space: None,
|
|
|
|
|
priority: Priority::normal(),
|
|
|
|
|
cpu_affinity: CpuSet::new_full(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Sets the function that represents the entry point of the task.
|
|
|
|
|
pub fn func<F>(mut self, func: F) -> Self
|
|
|
|
|
where
|
|
|
|
|
F: Fn() + Send + Sync + 'static,
|
|
|
|
|
{
|
|
|
|
|
self.func = Some(Box::new(func));
|
|
|
|
|
self
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Sets the data associated with the task.
|
|
|
|
|
pub fn data<T>(mut self, data: T) -> Self
|
|
|
|
|
where
|
|
|
|
|
T: Any + Send + Sync,
|
|
|
|
|
{
|
|
|
|
|
self.data = Some(Box::new(data));
|
|
|
|
|
self
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Sets the user space associated with the task.
|
|
|
|
|
pub fn user_space(mut self, user_space: Option<Arc<UserSpace>>) -> Self {
|
|
|
|
|
self.user_space = user_space;
|
|
|
|
|
self
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Sets the priority of the task.
|
|
|
|
|
pub fn priority(mut self, priority: Priority) -> Self {
|
|
|
|
|
self.priority = priority;
|
|
|
|
|
self
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Sets the CPU affinity mask for the task.
|
|
|
|
|
///
|
|
|
|
|
/// The `cpu_affinity` parameter represents
|
|
|
|
|
/// the desired set of CPUs to run the task on.
|
|
|
|
|
pub fn cpu_affinity(mut self, cpu_affinity: CpuSet) -> Self {
|
|
|
|
|
self.cpu_affinity = cpu_affinity;
|
|
|
|
|
self
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Builds a new task without running it immediately.
|
2024-09-13 03:13:07 +00:00
|
|
|
pub fn build(self) -> Result<Task> {
|
2024-08-22 11:23:33 +00:00
|
|
|
/// all task will entering this function
|
|
|
|
|
/// this function is mean to executing the task_fn in Task
|
|
|
|
|
extern "C" fn kernel_task_entry() {
|
|
|
|
|
let current_task = current_task()
|
|
|
|
|
.expect("no current task, it should have current task in kernel task entry");
|
|
|
|
|
current_task.func.call(());
|
|
|
|
|
current_task.exit();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let mut new_task = Task {
|
|
|
|
|
func: self.func.unwrap(),
|
|
|
|
|
data: self.data.unwrap(),
|
|
|
|
|
user_space: self.user_space,
|
|
|
|
|
ctx: UnsafeCell::new(TaskContext::default()),
|
|
|
|
|
kstack: KernelStack::new_with_guard_page()?,
|
|
|
|
|
schedule_info: TaskScheduleInfo {
|
|
|
|
|
cpu: AtomicCpuId::default(),
|
|
|
|
|
priority: self.priority,
|
|
|
|
|
cpu_affinity: self.cpu_affinity,
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let ctx = new_task.ctx.get_mut();
|
|
|
|
|
ctx.set_instruction_pointer(kernel_task_entry as usize);
|
|
|
|
|
// We should reserve space for the return address in the stack, otherwise
|
|
|
|
|
// we will write across the page boundary due to the implementation of
|
|
|
|
|
// the context switch.
|
|
|
|
|
//
|
|
|
|
|
// According to the System V AMD64 ABI, the stack pointer should be aligned
|
|
|
|
|
// to at least 16 bytes. And a larger alignment is needed if larger arguments
|
|
|
|
|
// are passed to the function. The `kernel_task_entry` function does not
|
|
|
|
|
// have any arguments, so we only need to align the stack pointer to 16 bytes.
|
|
|
|
|
ctx.set_stack_pointer(crate::mm::paddr_to_vaddr(new_task.kstack.end_paddr() - 16));
|
|
|
|
|
|
2024-09-13 03:13:07 +00:00
|
|
|
Ok(new_task)
|
2024-08-22 11:23:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Builds a new task and run it immediately.
|
|
|
|
|
pub fn spawn(self) -> Result<Arc<Task>> {
|
2024-09-13 03:13:07 +00:00
|
|
|
let task = Arc::new(self.build()?);
|
2024-08-22 11:23:33 +00:00
|
|
|
task.run();
|
|
|
|
|
Ok(task)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Trait for manipulating the task context.
|
|
|
|
|
pub trait TaskContextApi {
|
|
|
|
|
/// Sets instruction pointer
|
|
|
|
|
fn set_instruction_pointer(&mut self, ip: usize);
|
|
|
|
|
|
|
|
|
|
/// Gets instruction pointer
|
|
|
|
|
fn instruction_pointer(&self) -> usize;
|
|
|
|
|
|
|
|
|
|
/// Sets stack pointer
|
|
|
|
|
fn set_stack_pointer(&mut self, sp: usize);
|
|
|
|
|
|
|
|
|
|
/// Gets stack pointer
|
|
|
|
|
fn stack_pointer(&self) -> usize;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(ktest)]
|
|
|
|
|
mod test {
|
|
|
|
|
use crate::prelude::*;
|
|
|
|
|
|
|
|
|
|
#[ktest]
|
|
|
|
|
fn create_task() {
|
|
|
|
|
#[allow(clippy::eq_op)]
|
|
|
|
|
let task = || {
|
|
|
|
|
assert_eq!(1, 1);
|
|
|
|
|
};
|
2024-09-13 03:13:07 +00:00
|
|
|
let task = Arc::new(
|
|
|
|
|
crate::task::TaskOptions::new(task)
|
|
|
|
|
.data(())
|
|
|
|
|
.build()
|
|
|
|
|
.unwrap(),
|
|
|
|
|
);
|
|
|
|
|
task.run();
|
2024-08-22 11:23:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[ktest]
|
|
|
|
|
fn spawn_task() {
|
|
|
|
|
#[allow(clippy::eq_op)]
|
|
|
|
|
let task = || {
|
|
|
|
|
assert_eq!(1, 1);
|
|
|
|
|
};
|
|
|
|
|
let _ = crate::task::TaskOptions::new(task).data(()).spawn();
|
|
|
|
|
}
|
|
|
|
|
}
|