mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-23 01:13:23 +00:00
Introduce CPU clock and CPU timer
This commit is contained in:
committed by
Tate, Hongliang Tian
parent
f1c1011f2b
commit
c84efe7a90
@ -1,5 +1,7 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use core::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
use align_ext::AlignExt;
|
||||
use log::debug;
|
||||
#[cfg(feature = "intel_tdx")]
|
||||
@ -14,6 +16,7 @@ use crate::arch::{
|
||||
};
|
||||
use crate::{
|
||||
cpu::{CpuException, PageFaultErrorCode, PAGE_FAULT},
|
||||
cpu_local,
|
||||
trap::call_irq_callback_functions,
|
||||
vm::{
|
||||
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
|
||||
#[no_mangle]
|
||||
extern "sysv64" fn trap_handler(f: &mut TrapFrame) {
|
||||
@ -43,7 +57,9 @@ extern "sysv64" fn trap_handler(f: &mut TrapFrame) {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
IS_KERNEL_INTERRUPTED.store(true, Ordering::Release);
|
||||
call_irq_callback_functions(f);
|
||||
IS_KERNEL_INTERRUPTED.store(false, Ordering::Release);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -80,6 +80,7 @@ pub fn init() {
|
||||
device::init().unwrap();
|
||||
vdso::init();
|
||||
taskless::init();
|
||||
process::init();
|
||||
}
|
||||
|
||||
fn init_thread() {
|
||||
|
@ -32,3 +32,7 @@ pub use program_loader::{check_executable_file, load_program_to_vm};
|
||||
pub use rlimit::ResourceType;
|
||||
pub use term_status::TermStatus;
|
||||
pub use wait::{wait_child_exit, WaitOptions};
|
||||
|
||||
pub(super) fn init() {
|
||||
process::init();
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ use crate::{
|
||||
Credentials, Process,
|
||||
},
|
||||
thread::{status::ThreadStatus, task, thread_table, Thread, Tid},
|
||||
time::{clocks::ProfClock, TimerManager},
|
||||
};
|
||||
|
||||
/// The builder to build a posix thread
|
||||
@ -94,6 +95,11 @@ impl PosixThreadBuilder {
|
||||
let thread = Arc::new_cyclic(|thread_ref| {
|
||||
let task = task::create_new_user_task(user_space, thread_ref.clone());
|
||||
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 {
|
||||
process,
|
||||
is_main_thread,
|
||||
@ -106,6 +112,9 @@ impl PosixThreadBuilder {
|
||||
sig_context: Mutex::new(None),
|
||||
sig_stack: Mutex::new(None),
|
||||
robust_list: Mutex::new(None),
|
||||
prof_clock,
|
||||
virtual_timer_manager,
|
||||
prof_timer_manager,
|
||||
};
|
||||
|
||||
Thread::new(tid, task, posix_thread, status)
|
||||
|
@ -21,6 +21,7 @@ use crate::{
|
||||
prelude::*,
|
||||
process::signal::constants::SIGCONT,
|
||||
thread::{thread_table, Tid},
|
||||
time::{clocks::ProfClock, Timer, TimerManager},
|
||||
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.
|
||||
sig_context: Mutex<Option<Vaddr>>,
|
||||
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 {
|
||||
@ -148,10 +158,39 @@ impl PosixThread {
|
||||
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);
|
||||
}
|
||||
|
||||
/// 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>> {
|
||||
self.sig_queues.dequeue(mask)
|
||||
}
|
||||
|
@ -1,15 +1,13 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use self::timer_manager::PosixTimerManager;
|
||||
use super::{
|
||||
posix_thread::PosixThreadExt,
|
||||
process_table,
|
||||
process_vm::{Heap, InitStackReader, ProcessVm},
|
||||
rlimit::ResourceLimits,
|
||||
signal::{
|
||||
constants::{SIGALRM, SIGCHLD},
|
||||
sig_disposition::SigDispositions,
|
||||
sig_mask::SigMask,
|
||||
signals::{kernel::KernelSignal, Signal},
|
||||
constants::SIGCHLD, sig_disposition::SigDispositions, sig_mask::SigMask, signals::Signal,
|
||||
Pauser,
|
||||
},
|
||||
status::ProcessStatus,
|
||||
@ -20,12 +18,8 @@ use crate::{
|
||||
fs::{file_table::FileTable, fs_resolver::FsResolver, utils::FileCreationMask},
|
||||
prelude::*,
|
||||
sched::nice::Nice,
|
||||
thread::{
|
||||
allocate_tid,
|
||||
work_queue::{submit_work_item, work_item::WorkItem},
|
||||
Thread,
|
||||
},
|
||||
time::{clocks::RealTimeClock, Timer},
|
||||
thread::{allocate_tid, Thread},
|
||||
time::clocks::ProfClock,
|
||||
vm::vmar::Vmar,
|
||||
};
|
||||
|
||||
@ -34,6 +28,7 @@ mod job_control;
|
||||
mod process_group;
|
||||
mod session;
|
||||
mod terminal;
|
||||
mod timer_manager;
|
||||
|
||||
use aster_rights::Full;
|
||||
use atomic::Atomic;
|
||||
@ -52,6 +47,10 @@ pub type Sid = u32;
|
||||
|
||||
pub type ExitCode = i32;
|
||||
|
||||
pub(super) fn init() {
|
||||
timer_manager::init();
|
||||
}
|
||||
|
||||
/// Process stands for a set of threads that shares the same userspace.
|
||||
pub struct Process {
|
||||
// Immutable Part
|
||||
@ -60,8 +59,7 @@ pub struct Process {
|
||||
process_vm: ProcessVm,
|
||||
/// Wait for child status changed
|
||||
children_pauser: Arc<Pauser>,
|
||||
/// The timer counts down in real (i.e., wall clock) time
|
||||
alarm_timer: Arc<Timer>,
|
||||
|
||||
// Mutable Part
|
||||
/// The executable path.
|
||||
executable_path: RwLock<String>,
|
||||
@ -91,26 +89,12 @@ pub struct Process {
|
||||
// Signal
|
||||
/// Sig dispositions
|
||||
sig_dispositions: Arc<Mutex<SigDispositions>>,
|
||||
}
|
||||
|
||||
fn create_process_timer_callback(process_ref: &Weak<Process>) -> impl Fn() {
|
||||
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);
|
||||
}
|
||||
};
|
||||
/// A profiling clock measures the user CPU time and kernel CPU time of the current process.
|
||||
prof_clock: Arc<ProfClock>,
|
||||
|
||||
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,
|
||||
);
|
||||
}
|
||||
/// A manager that manages timer resources and utilities of the process.
|
||||
timer_manager: PosixTimerManager,
|
||||
}
|
||||
|
||||
impl Process {
|
||||
@ -135,28 +119,26 @@ impl Process {
|
||||
Pauser::new_with_mask(sigmask)
|
||||
};
|
||||
|
||||
Arc::new_cyclic(|process_ref: &Weak<Process>| {
|
||||
let callback = create_process_timer_callback(process_ref);
|
||||
let alarm_timer = RealTimeClock::timer_manager().create_timer(callback);
|
||||
let prof_clock = ProfClock::new();
|
||||
|
||||
Self {
|
||||
pid,
|
||||
threads: Mutex::new(threads),
|
||||
executable_path: RwLock::new(executable_path),
|
||||
process_vm,
|
||||
children_pauser,
|
||||
alarm_timer,
|
||||
status: Mutex::new(ProcessStatus::Uninit),
|
||||
parent: Mutex::new(parent),
|
||||
children: Mutex::new(BTreeMap::new()),
|
||||
process_group: Mutex::new(Weak::new()),
|
||||
file_table,
|
||||
fs,
|
||||
umask,
|
||||
sig_dispositions,
|
||||
resource_limits: Mutex::new(resource_limits),
|
||||
nice: Atomic::new(nice),
|
||||
}
|
||||
Arc::new_cyclic(|process_ref: &Weak<Process>| Self {
|
||||
pid,
|
||||
threads: Mutex::new(threads),
|
||||
executable_path: RwLock::new(executable_path),
|
||||
process_vm,
|
||||
children_pauser,
|
||||
status: Mutex::new(ProcessStatus::Uninit),
|
||||
parent: Mutex::new(parent),
|
||||
children: Mutex::new(BTreeMap::new()),
|
||||
process_group: Mutex::new(Weak::new()),
|
||||
file_table,
|
||||
fs,
|
||||
umask,
|
||||
sig_dispositions,
|
||||
resource_limits: Mutex::new(resource_limits),
|
||||
nice: Atomic::new(nice),
|
||||
timer_manager: PosixTimerManager::new(&prof_clock, process_ref),
|
||||
prof_clock,
|
||||
})
|
||||
}
|
||||
|
||||
@ -233,8 +215,14 @@ impl Process {
|
||||
self.pid
|
||||
}
|
||||
|
||||
pub fn alarm_timer(&self) -> &Arc<Timer> {
|
||||
&self.alarm_timer
|
||||
/// Gets the profiling clock of the process.
|
||||
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>>> {
|
||||
@ -288,7 +276,7 @@ impl Process {
|
||||
|
||||
// *********** Process group & Session***********
|
||||
|
||||
/// Returns the process group id of the process.
|
||||
/// Returns the process group ID of the process.
|
||||
pub fn pgid(&self) -> Pgid {
|
||||
if let Some(process_group) = self.process_group.lock().upgrade() {
|
||||
process_group.pgid()
|
||||
@ -341,7 +329,7 @@ impl Process {
|
||||
///
|
||||
/// This method may return the following errors:
|
||||
/// * `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>> {
|
||||
if self.is_session_leader() {
|
||||
return Ok(self.session().unwrap());
|
||||
@ -439,7 +427,7 @@ impl Process {
|
||||
if pgid != self.pid() {
|
||||
return_errno_with_message!(
|
||||
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)]
|
||||
mod test {
|
||||
|
||||
use spin::Once;
|
||||
|
||||
use super::*;
|
||||
|
||||
fn new_process(parent: Option<Arc<Process>>) -> Arc<Process> {
|
||||
|
200
kernel/aster-nix/src/process/process/timer_manager.rs
Normal file
200
kernel/aster-nix/src/process/process/timer_manager.rs
Normal 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
|
||||
}
|
||||
}
|
67
kernel/aster-nix/src/time/clocks/cpu_clock.rs
Normal file
67
kernel/aster-nix/src/time/clocks/cpu_clock.rs
Normal 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()
|
||||
}
|
||||
}
|
@ -1,7 +1,9 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
pub use cpu_clock::*;
|
||||
pub use system_wide::*;
|
||||
|
||||
mod cpu_clock;
|
||||
mod system_wide;
|
||||
|
||||
pub(super) fn init() {
|
||||
|
@ -124,6 +124,11 @@ impl BootTimeClock {
|
||||
pub fn get() -> &'static Arc<BootTimeClock> {
|
||||
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 {
|
||||
@ -232,7 +237,7 @@ define_system_clocks! {
|
||||
CLOCK_BOOTTIME => BootTimeClock,
|
||||
}
|
||||
|
||||
define_timer_managers![CLOCK_REALTIME, CLOCK_MONOTONIC,];
|
||||
define_timer_managers![CLOCK_REALTIME, CLOCK_MONOTONIC, CLOCK_BOOTTIME,];
|
||||
|
||||
/// Init the system-wide clocks.
|
||||
fn init_system_wide_clocks() {
|
||||
|
Reference in New Issue
Block a user