Introduce CPU clock and CPU timer

This commit is contained in:
Chen Chengjun
2024-05-31 18:11:52 +08:00
committed by Tate, Hongliang Tian
parent f1c1011f2b
commit c84efe7a90
10 changed files with 389 additions and 60 deletions

View File

@ -1,5 +1,7 @@
// SPDX-License-Identifier: MPL-2.0 // SPDX-License-Identifier: MPL-2.0
use core::sync::atomic::{AtomicBool, Ordering};
use align_ext::AlignExt; use align_ext::AlignExt;
use log::debug; use log::debug;
#[cfg(feature = "intel_tdx")] #[cfg(feature = "intel_tdx")]
@ -14,6 +16,7 @@ use crate::arch::{
}; };
use crate::{ use crate::{
cpu::{CpuException, PageFaultErrorCode, PAGE_FAULT}, cpu::{CpuException, PageFaultErrorCode, PAGE_FAULT},
cpu_local,
trap::call_irq_callback_functions, trap::call_irq_callback_functions,
vm::{ vm::{
kspace::{KERNEL_PAGE_TABLE, LINEAR_MAPPING_BASE_VADDR, LINEAR_MAPPING_VADDR_RANGE}, kspace::{KERNEL_PAGE_TABLE, LINEAR_MAPPING_BASE_VADDR, LINEAR_MAPPING_VADDR_RANGE},
@ -22,6 +25,17 @@ use crate::{
}, },
}; };
cpu_local! {
static IS_KERNEL_INTERRUPTED: AtomicBool = AtomicBool::new(false);
}
/// Returns true if this function is called within the context of an IRQ handler
/// and the IRQ occurs while the CPU is executing in the kernel mode.
/// Otherwise, it returns false.
pub fn is_kernel_interrupted() -> bool {
IS_KERNEL_INTERRUPTED.load(Ordering::Acquire)
}
/// Only from kernel /// Only from kernel
#[no_mangle] #[no_mangle]
extern "sysv64" fn trap_handler(f: &mut TrapFrame) { extern "sysv64" fn trap_handler(f: &mut TrapFrame) {
@ -43,7 +57,9 @@ extern "sysv64" fn trap_handler(f: &mut TrapFrame) {
} }
} }
} else { } else {
IS_KERNEL_INTERRUPTED.store(true, Ordering::Release);
call_irq_callback_functions(f); call_irq_callback_functions(f);
IS_KERNEL_INTERRUPTED.store(false, Ordering::Release);
} }
} }

View File

@ -80,6 +80,7 @@ pub fn init() {
device::init().unwrap(); device::init().unwrap();
vdso::init(); vdso::init();
taskless::init(); taskless::init();
process::init();
} }
fn init_thread() { fn init_thread() {

View File

@ -32,3 +32,7 @@ pub use program_loader::{check_executable_file, load_program_to_vm};
pub use rlimit::ResourceType; pub use rlimit::ResourceType;
pub use term_status::TermStatus; pub use term_status::TermStatus;
pub use wait::{wait_child_exit, WaitOptions}; pub use wait::{wait_child_exit, WaitOptions};
pub(super) fn init() {
process::init();
}

View File

@ -11,6 +11,7 @@ use crate::{
Credentials, Process, Credentials, Process,
}, },
thread::{status::ThreadStatus, task, thread_table, Thread, Tid}, thread::{status::ThreadStatus, task, thread_table, Thread, Tid},
time::{clocks::ProfClock, TimerManager},
}; };
/// The builder to build a posix thread /// The builder to build a posix thread
@ -94,6 +95,11 @@ impl PosixThreadBuilder {
let thread = Arc::new_cyclic(|thread_ref| { let thread = Arc::new_cyclic(|thread_ref| {
let task = task::create_new_user_task(user_space, thread_ref.clone()); let task = task::create_new_user_task(user_space, thread_ref.clone());
let status = ThreadStatus::Init; let status = ThreadStatus::Init;
let prof_clock = ProfClock::new();
let virtual_timer_manager = TimerManager::new(prof_clock.user_clock().clone());
let prof_timer_manager = TimerManager::new(prof_clock.clone());
let posix_thread = PosixThread { let posix_thread = PosixThread {
process, process,
is_main_thread, is_main_thread,
@ -106,6 +112,9 @@ impl PosixThreadBuilder {
sig_context: Mutex::new(None), sig_context: Mutex::new(None),
sig_stack: Mutex::new(None), sig_stack: Mutex::new(None),
robust_list: Mutex::new(None), robust_list: Mutex::new(None),
prof_clock,
virtual_timer_manager,
prof_timer_manager,
}; };
Thread::new(tid, task, posix_thread, status) Thread::new(tid, task, posix_thread, status)

View File

@ -21,6 +21,7 @@ use crate::{
prelude::*, prelude::*,
process::signal::constants::SIGCONT, process::signal::constants::SIGCONT,
thread::{thread_table, Tid}, thread::{thread_table, Tid},
time::{clocks::ProfClock, Timer, TimerManager},
util::write_val_to_user, util::write_val_to_user,
}; };
@ -62,6 +63,15 @@ pub struct PosixThread {
/// FIXME: This field may be removed. For glibc applications with RESTORER flag set, the sig_context is always equals with rsp. /// FIXME: This field may be removed. For glibc applications with RESTORER flag set, the sig_context is always equals with rsp.
sig_context: Mutex<Option<Vaddr>>, sig_context: Mutex<Option<Vaddr>>,
sig_stack: Mutex<Option<SigStack>>, sig_stack: Mutex<Option<SigStack>>,
/// A profiling clock measures the user CPU time and kernel CPU time in the thread.
prof_clock: Arc<ProfClock>,
/// A manager that manages timers based on the user CPU time of the current thread.
virtual_timer_manager: Arc<TimerManager>,
/// A manager that manages timers based on the profiling clock of the current thread.
prof_timer_manager: Arc<TimerManager>,
} }
impl PosixThread { impl PosixThread {
@ -148,10 +158,39 @@ impl PosixThread {
return_errno_with_message!(Errno::EPERM, "sending signal to the thread is not allowed."); return_errno_with_message!(Errno::EPERM, "sending signal to the thread is not allowed.");
} }
pub(in crate::process) fn enqueue_signal(&self, signal: Box<dyn Signal>) { /// Enqueues a thread-directed signal. This method should only be used for enqueue kernel
/// signal and fault signal.
pub fn enqueue_signal(&self, signal: Box<dyn Signal>) {
self.sig_queues.enqueue(signal); self.sig_queues.enqueue(signal);
} }
/// Returns a reference to the profiling clock of the current thread.
pub fn prof_clock(&self) -> &Arc<ProfClock> {
&self.prof_clock
}
/// Creates a timer based on the profiling CPU clock of the current thread.
pub fn create_prof_timer<F>(&self, func: F) -> Arc<Timer>
where
F: Fn() + Send + Sync + 'static,
{
self.prof_timer_manager.create_timer(func)
}
/// Creates a timer based on the user CPU clock of the current thread.
pub fn create_virtual_timer<F>(&self, func: F) -> Arc<Timer>
where
F: Fn() + Send + Sync + 'static,
{
self.virtual_timer_manager.create_timer(func)
}
/// Checks the `TimerCallback`s that are managed by the `prof_timer_manager`.
/// If any have timed out, call the corresponding callback functions.
pub fn process_expired_timers(&self) {
self.prof_timer_manager.process_expired_timers();
}
pub fn dequeue_signal(&self, mask: &SigMask) -> Option<Box<dyn Signal>> { pub fn dequeue_signal(&self, mask: &SigMask) -> Option<Box<dyn Signal>> {
self.sig_queues.dequeue(mask) self.sig_queues.dequeue(mask)
} }

View File

@ -1,15 +1,13 @@
// SPDX-License-Identifier: MPL-2.0 // SPDX-License-Identifier: MPL-2.0
use self::timer_manager::PosixTimerManager;
use super::{ use super::{
posix_thread::PosixThreadExt, posix_thread::PosixThreadExt,
process_table, process_table,
process_vm::{Heap, InitStackReader, ProcessVm}, process_vm::{Heap, InitStackReader, ProcessVm},
rlimit::ResourceLimits, rlimit::ResourceLimits,
signal::{ signal::{
constants::{SIGALRM, SIGCHLD}, constants::SIGCHLD, sig_disposition::SigDispositions, sig_mask::SigMask, signals::Signal,
sig_disposition::SigDispositions,
sig_mask::SigMask,
signals::{kernel::KernelSignal, Signal},
Pauser, Pauser,
}, },
status::ProcessStatus, status::ProcessStatus,
@ -20,12 +18,8 @@ use crate::{
fs::{file_table::FileTable, fs_resolver::FsResolver, utils::FileCreationMask}, fs::{file_table::FileTable, fs_resolver::FsResolver, utils::FileCreationMask},
prelude::*, prelude::*,
sched::nice::Nice, sched::nice::Nice,
thread::{ thread::{allocate_tid, Thread},
allocate_tid, time::clocks::ProfClock,
work_queue::{submit_work_item, work_item::WorkItem},
Thread,
},
time::{clocks::RealTimeClock, Timer},
vm::vmar::Vmar, vm::vmar::Vmar,
}; };
@ -34,6 +28,7 @@ mod job_control;
mod process_group; mod process_group;
mod session; mod session;
mod terminal; mod terminal;
mod timer_manager;
use aster_rights::Full; use aster_rights::Full;
use atomic::Atomic; use atomic::Atomic;
@ -52,6 +47,10 @@ pub type Sid = u32;
pub type ExitCode = i32; pub type ExitCode = i32;
pub(super) fn init() {
timer_manager::init();
}
/// Process stands for a set of threads that shares the same userspace. /// Process stands for a set of threads that shares the same userspace.
pub struct Process { pub struct Process {
// Immutable Part // Immutable Part
@ -60,8 +59,7 @@ pub struct Process {
process_vm: ProcessVm, process_vm: ProcessVm,
/// Wait for child status changed /// Wait for child status changed
children_pauser: Arc<Pauser>, children_pauser: Arc<Pauser>,
/// The timer counts down in real (i.e., wall clock) time
alarm_timer: Arc<Timer>,
// Mutable Part // Mutable Part
/// The executable path. /// The executable path.
executable_path: RwLock<String>, executable_path: RwLock<String>,
@ -91,26 +89,12 @@ pub struct Process {
// Signal // Signal
/// Sig dispositions /// Sig dispositions
sig_dispositions: Arc<Mutex<SigDispositions>>, sig_dispositions: Arc<Mutex<SigDispositions>>,
}
fn create_process_timer_callback(process_ref: &Weak<Process>) -> impl Fn() { /// A profiling clock measures the user CPU time and kernel CPU time of the current process.
let current_process = process_ref.clone(); prof_clock: Arc<ProfClock>,
let sent_signal = move || {
let signal = KernelSignal::new(SIGALRM);
if let Some(process) = current_process.upgrade() {
process.enqueue_signal(signal);
}
};
let work_func = Box::new(sent_signal); /// A manager that manages timer resources and utilities of the process.
let work_item = Arc::new(WorkItem::new(work_func)); timer_manager: PosixTimerManager,
move || {
submit_work_item(
work_item.clone(),
crate::thread::work_queue::WorkPriority::High,
);
}
} }
impl Process { impl Process {
@ -135,17 +119,14 @@ impl Process {
Pauser::new_with_mask(sigmask) Pauser::new_with_mask(sigmask)
}; };
Arc::new_cyclic(|process_ref: &Weak<Process>| { let prof_clock = ProfClock::new();
let callback = create_process_timer_callback(process_ref);
let alarm_timer = RealTimeClock::timer_manager().create_timer(callback);
Self { Arc::new_cyclic(|process_ref: &Weak<Process>| Self {
pid, pid,
threads: Mutex::new(threads), threads: Mutex::new(threads),
executable_path: RwLock::new(executable_path), executable_path: RwLock::new(executable_path),
process_vm, process_vm,
children_pauser, children_pauser,
alarm_timer,
status: Mutex::new(ProcessStatus::Uninit), status: Mutex::new(ProcessStatus::Uninit),
parent: Mutex::new(parent), parent: Mutex::new(parent),
children: Mutex::new(BTreeMap::new()), children: Mutex::new(BTreeMap::new()),
@ -156,7 +137,8 @@ impl Process {
sig_dispositions, sig_dispositions,
resource_limits: Mutex::new(resource_limits), resource_limits: Mutex::new(resource_limits),
nice: Atomic::new(nice), nice: Atomic::new(nice),
} timer_manager: PosixTimerManager::new(&prof_clock, process_ref),
prof_clock,
}) })
} }
@ -233,8 +215,14 @@ impl Process {
self.pid self.pid
} }
pub fn alarm_timer(&self) -> &Arc<Timer> { /// Gets the profiling clock of the process.
&self.alarm_timer pub fn prof_clock(&self) -> &Arc<ProfClock> {
&self.prof_clock
}
/// Gets the timer resources and utilities of the process.
pub fn timer_manager(&self) -> &PosixTimerManager {
&self.timer_manager
} }
pub fn threads(&self) -> &Mutex<Vec<Arc<Thread>>> { pub fn threads(&self) -> &Mutex<Vec<Arc<Thread>>> {
@ -288,7 +276,7 @@ impl Process {
// *********** Process group & Session*********** // *********** Process group & Session***********
/// Returns the process group id of the process. /// Returns the process group ID of the process.
pub fn pgid(&self) -> Pgid { pub fn pgid(&self) -> Pgid {
if let Some(process_group) = self.process_group.lock().upgrade() { if let Some(process_group) = self.process_group.lock().upgrade() {
process_group.pgid() process_group.pgid()
@ -341,7 +329,7 @@ impl Process {
/// ///
/// This method may return the following errors: /// This method may return the following errors:
/// * `EPERM`, if the process is a process group leader, or some existing session /// * `EPERM`, if the process is a process group leader, or some existing session
/// or process group has the same id as the process. /// or process group has the same ID as the process.
pub fn to_new_session(self: &Arc<Self>) -> Result<Arc<Session>> { pub fn to_new_session(self: &Arc<Self>) -> Result<Arc<Session>> {
if self.is_session_leader() { if self.is_session_leader() {
return Ok(self.session().unwrap()); return Ok(self.session().unwrap());
@ -439,7 +427,7 @@ impl Process {
if pgid != self.pid() { if pgid != self.pid() {
return_errno_with_message!( return_errno_with_message!(
Errno::EPERM, Errno::EPERM,
"the new process group should have the same id as the process." "the new process group should have the same ID as the process."
); );
} }
@ -637,8 +625,6 @@ pub fn current() -> Arc<Process> {
#[cfg(ktest)] #[cfg(ktest)]
mod test { mod test {
use spin::Once;
use super::*; use super::*;
fn new_process(parent: Option<Arc<Process>>) -> Arc<Process> { fn new_process(parent: Option<Arc<Process>>) -> Arc<Process> {

View File

@ -0,0 +1,200 @@
// SPDX-License-Identifier: MPL-2.0
use alloc::{
boxed::Box,
sync::{Arc, Weak},
vec::Vec,
};
use core::time::Duration;
use aster_frame::{
arch::{
timer::{self, TIMER_FREQ},
x86::trap::is_kernel_interrupted,
},
sync::Mutex,
};
use id_alloc::IdAlloc;
use super::Process;
use crate::{
process::{
posix_thread::PosixThreadExt,
signal::{constants::SIGALRM, signals::kernel::KernelSignal},
},
thread::{
work_queue::{submit_work_item, work_item::WorkItem},
Thread,
},
time::{
clocks::{ProfClock, RealTimeClock},
Timer, TimerManager,
},
};
/// Updates the CPU time recorded in the CPU clocks of current Process.
///
/// This function will be invoked at the system timer interrupt, and
/// invoke the callbacks of expired timers which are based on the updated
/// CPU clock.
fn update_cpu_time() {
let current_thread = Thread::current();
if let Some(posix_thread) = current_thread.as_posix_thread() {
let process = posix_thread.process();
let timer_manager = process.timer_manager();
let jiffies_interval = Duration::from_millis(1000 / TIMER_FREQ);
// Based on whether the timer interrupt occurs in kernel mode or user mode,
// the function will add the duration of one timer interrupt interval to the
// corresponding CPU clocks.
if is_kernel_interrupted() {
posix_thread
.prof_clock()
.kernel_clock()
.add_time(jiffies_interval);
process
.prof_clock()
.kernel_clock()
.add_time(jiffies_interval);
} else {
posix_thread
.prof_clock()
.user_clock()
.add_time(jiffies_interval);
process.prof_clock().user_clock().add_time(jiffies_interval);
timer_manager
.virtual_timer()
.timer_manager()
.process_expired_timers();
}
timer_manager
.prof_timer()
.timer_manager()
.process_expired_timers();
posix_thread.process_expired_timers();
}
}
/// Registers a function to update the CPU clock in processes and
/// threads during the system timer interrupt.
pub(super) fn init() {
timer::register_callback(update_cpu_time);
}
/// Represents timer resources and utilities for a POSIX process.
pub struct PosixTimerManager {
/// A real-time countdown timer, measuring in wall clock time.
alarm_timer: Arc<Timer>,
/// A timer based on user CPU clock.
virtual_timer: Arc<Timer>,
/// A timer based on the profiling clock.
prof_timer: Arc<Timer>,
/// An ID allocator to allocate unique timer IDs.
id_allocator: Mutex<IdAlloc>,
/// A container managing all POSIX timers created by `timer_create()` syscall
/// within the process context.
posix_timers: Mutex<Vec<Option<Arc<Timer>>>>,
}
fn create_process_timer_callback(process_ref: &Weak<Process>) -> impl Fn() + Clone {
let current_process = process_ref.clone();
let sent_signal = move || {
let signal = KernelSignal::new(SIGALRM);
if let Some(process) = current_process.upgrade() {
process.enqueue_signal(signal);
}
};
let work_func = Box::new(sent_signal);
let work_item = Arc::new(WorkItem::new(work_func));
move || {
submit_work_item(
work_item.clone(),
crate::thread::work_queue::WorkPriority::High,
);
}
}
impl PosixTimerManager {
pub(super) fn new(prof_clock: &Arc<ProfClock>, process_ref: &Weak<Process>) -> Self {
const MAX_NUM_OF_POSIX_TIMERS: usize = 10000;
let callback = create_process_timer_callback(process_ref);
let alarm_timer = RealTimeClock::timer_manager().create_timer(callback.clone());
let virtual_timer =
TimerManager::new(prof_clock.user_clock().clone()).create_timer(callback.clone());
let prof_timer = TimerManager::new(prof_clock.clone()).create_timer(callback);
Self {
alarm_timer,
virtual_timer,
prof_timer,
id_allocator: Mutex::new(IdAlloc::with_capacity(MAX_NUM_OF_POSIX_TIMERS)),
posix_timers: Mutex::new(Vec::new()),
}
}
/// Gets the alarm timer of the corresponding process.
pub fn alarm_timer(&self) -> &Arc<Timer> {
&self.alarm_timer
}
/// Gets the virtual timer of the corresponding process.
pub fn virtual_timer(&self) -> &Arc<Timer> {
&self.virtual_timer
}
/// Gets the profiling timer of the corresponding process.
pub fn prof_timer(&self) -> &Arc<Timer> {
&self.prof_timer
}
/// Creates a timer based on the profiling CPU clock of the current process.
pub fn create_prof_timer<F>(&self, func: F) -> Arc<Timer>
where
F: Fn() + Send + Sync + 'static,
{
self.prof_timer.timer_manager().create_timer(func)
}
/// Creates a timer based on the user CPU clock of the current process.
pub fn create_virtual_timer<F>(&self, func: F) -> Arc<Timer>
where
F: Fn() + Send + Sync + 'static,
{
self.virtual_timer.timer_manager().create_timer(func)
}
/// Adds a POSIX timer to the managed `posix_timers`, and allocate a timer ID for this timer.
/// Return the timer ID.
pub fn add_posix_timer(&self, posix_timer: Arc<Timer>) -> usize {
let mut timers = self.posix_timers.lock();
// Holding the lock of `posix_timers` is required to operate the `id_allocator`.
let timer_id = self.id_allocator.lock().alloc().unwrap();
if timers.len() < timer_id + 1 {
timers.resize(timer_id + 1, None);
}
// The ID allocated is not used by any other timers so this index in `timers`
// must be `None`.
timers[timer_id] = Some(posix_timer);
timer_id
}
/// Finds a POSIX timer by the input `timer_id`.
pub fn find_posix_timer(&self, timer_id: usize) -> Option<Arc<Timer>> {
self.posix_timers.lock()[timer_id].clone()
}
/// Removes the POSIX timer with the ID `timer_id`.
pub fn remove_posix_timer(&self, timer_id: usize) -> Option<Arc<Timer>> {
let mut timers = self.posix_timers.lock();
let timer = timers[timer_id].take();
if timer.is_some() {
// Holding the lock of `posix_timers` is required to operate the `id_allocator`.
self.id_allocator.lock().free(timer_id);
}
timer
}
}

View File

@ -0,0 +1,67 @@
// SPDX-License-Identifier: MPL-2.0
use alloc::sync::Arc;
use core::time::Duration;
use aster_frame::sync::SpinLock;
use crate::time::Clock;
/// A clock used to record the CPU time for processes and threads.
pub struct CpuClock {
time: SpinLock<Duration>,
}
/// A profiling clock that contains a user CPU clock and a kernel CPU clock.
/// These two clocks record the CPU time in user mode and kernel mode respectively.
/// Reading this clock directly returns the sum of both times.
pub struct ProfClock {
user_clock: Arc<CpuClock>,
kernel_clock: Arc<CpuClock>,
}
impl CpuClock {
/// Creates a new `CpuClock`. The recorded time is initialized to 0.
pub fn new() -> Arc<Self> {
Arc::new(Self {
time: SpinLock::new(Duration::ZERO),
})
}
/// Adds `interval` to the original recorded time to update the `CpuClock`.
pub fn add_time(&self, interval: Duration) {
*self.time.lock_irq_disabled() += interval;
}
}
impl Clock for CpuClock {
fn read_time(&self) -> Duration {
*self.time.lock_irq_disabled()
}
}
impl ProfClock {
/// Creates a new `ProfClock`. The recorded time is initialized to 0.
pub fn new() -> Arc<Self> {
Arc::new(Self {
user_clock: CpuClock::new(),
kernel_clock: CpuClock::new(),
})
}
/// Returns a reference to the user CPU clock in this profiling clock.
pub fn user_clock(&self) -> &Arc<CpuClock> {
&self.user_clock
}
/// Returns a reference to the kernel CPU clock in this profiling clock.
pub fn kernel_clock(&self) -> &Arc<CpuClock> {
&self.kernel_clock
}
}
impl Clock for ProfClock {
fn read_time(&self) -> Duration {
self.user_clock.read_time() + self.kernel_clock.read_time()
}
}

View File

@ -1,7 +1,9 @@
// SPDX-License-Identifier: MPL-2.0 // SPDX-License-Identifier: MPL-2.0
pub use cpu_clock::*;
pub use system_wide::*; pub use system_wide::*;
mod cpu_clock;
mod system_wide; mod system_wide;
pub(super) fn init() { pub(super) fn init() {

View File

@ -124,6 +124,11 @@ impl BootTimeClock {
pub fn get() -> &'static Arc<BootTimeClock> { pub fn get() -> &'static Arc<BootTimeClock> {
CLOCK_BOOTTIME_INSTANCE.get().unwrap() CLOCK_BOOTTIME_INSTANCE.get().unwrap()
} }
/// Get the cpu-local system-wide `TimerManager` singleton of this clock.
pub fn timer_manager() -> &'static Arc<TimerManager> {
CLOCK_BOOTTIME_MANAGER.get().unwrap()
}
} }
impl Clock for JiffiesClock { impl Clock for JiffiesClock {
@ -232,7 +237,7 @@ define_system_clocks! {
CLOCK_BOOTTIME => BootTimeClock, CLOCK_BOOTTIME => BootTimeClock,
} }
define_timer_managers![CLOCK_REALTIME, CLOCK_MONOTONIC,]; define_timer_managers![CLOCK_REALTIME, CLOCK_MONOTONIC, CLOCK_BOOTTIME,];
/// Init the system-wide clocks. /// Init the system-wide clocks.
fn init_system_wide_clocks() { fn init_system_wide_clocks() {