mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-15 00:06:47 +00:00
Fix the logics for the coarse resolution clock id in VDSO.
This commit is contained in:
parent
ff3ff0a598
commit
c3d0c59041
@ -68,6 +68,7 @@ pub mod vm;
|
|||||||
|
|
||||||
pub fn init() {
|
pub fn init() {
|
||||||
driver::init();
|
driver::init();
|
||||||
|
time::init();
|
||||||
net::init();
|
net::init();
|
||||||
sched::init();
|
sched::init();
|
||||||
fs::rootfs::init(boot::initramfs()).unwrap();
|
fs::rootfs::init(boot::initramfs()).unwrap();
|
||||||
|
@ -9,13 +9,17 @@ use crate::prelude::*;
|
|||||||
|
|
||||||
mod system_time;
|
mod system_time;
|
||||||
|
|
||||||
pub use system_time::SystemTime;
|
pub use system_time::{SystemTime, START_TIME};
|
||||||
|
|
||||||
pub type clockid_t = i32;
|
pub type clockid_t = i32;
|
||||||
pub type time_t = i64;
|
pub type time_t = i64;
|
||||||
pub type suseconds_t = i64;
|
pub type suseconds_t = i64;
|
||||||
pub type clock_t = i64;
|
pub type clock_t = i64;
|
||||||
|
|
||||||
|
pub(super) fn init() {
|
||||||
|
system_time::init_start_time();
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, TryFromInt, PartialEq)]
|
#[derive(Debug, Copy, Clone, TryFromInt, PartialEq)]
|
||||||
#[repr(i32)]
|
#[repr(i32)]
|
||||||
pub enum ClockID {
|
pub enum ClockID {
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
use core::time::Duration;
|
use core::time::Duration;
|
||||||
|
|
||||||
use aster_time::{read_monotonic_time, read_start_time};
|
use aster_time::{read_monotonic_time, read_start_time};
|
||||||
|
use spin::Once;
|
||||||
use time::{Date, Month, PrimitiveDateTime, Time};
|
use time::{Date, Month, PrimitiveDateTime, Time};
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
@ -11,6 +12,13 @@ use crate::prelude::*;
|
|||||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
pub struct SystemTime(PrimitiveDateTime);
|
pub struct SystemTime(PrimitiveDateTime);
|
||||||
|
|
||||||
|
pub static START_TIME: Once<SystemTime> = Once::new();
|
||||||
|
|
||||||
|
pub(super) fn init_start_time() {
|
||||||
|
let start_time = convert_system_time(read_start_time()).unwrap();
|
||||||
|
START_TIME.call_once(|| start_time);
|
||||||
|
}
|
||||||
|
|
||||||
impl SystemTime {
|
impl SystemTime {
|
||||||
/// The unix epoch, which represents 1970-01-01 00:00:00
|
/// The unix epoch, which represents 1970-01-01 00:00:00
|
||||||
pub const UNIX_EPOCH: SystemTime = SystemTime::unix_epoch();
|
pub const UNIX_EPOCH: SystemTime = SystemTime::unix_epoch();
|
||||||
@ -29,10 +37,8 @@ impl SystemTime {
|
|||||||
|
|
||||||
/// Returns the current system time
|
/// Returns the current system time
|
||||||
pub fn now() -> Self {
|
pub fn now() -> Self {
|
||||||
let start = read_start_time();
|
START_TIME
|
||||||
|
.get()
|
||||||
// The get real time result should always be valid
|
|
||||||
convert_system_time(start)
|
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.checked_add(read_monotonic_time())
|
.checked_add(read_monotonic_time())
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
@ -12,20 +12,22 @@
|
|||||||
//! use. It also hooks up the VDSO data update routine to the time management subsystem for periodic updates.
|
//! use. It also hooks up the VDSO data update routine to the time management subsystem for periodic updates.
|
||||||
|
|
||||||
use alloc::{boxed::Box, sync::Arc};
|
use alloc::{boxed::Box, sync::Arc};
|
||||||
|
use core::time::Duration;
|
||||||
|
|
||||||
use aster_frame::{
|
use aster_frame::{
|
||||||
sync::Mutex,
|
sync::SpinLock,
|
||||||
vm::{VmIo, PAGE_SIZE},
|
timer::Timer,
|
||||||
|
vm::{VmFrame, VmIo, PAGE_SIZE},
|
||||||
};
|
};
|
||||||
use aster_rights::Rights;
|
use aster_rights::Rights;
|
||||||
use aster_time::Instant;
|
use aster_time::{read_monotonic_time, Instant};
|
||||||
use aster_util::coeff::Coeff;
|
use aster_util::coeff::Coeff;
|
||||||
use pod::Pod;
|
use pod::Pod;
|
||||||
use spin::Once;
|
use spin::Once;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
fs::fs_resolver::{FsPath, FsResolver, AT_FDCWD},
|
fs::fs_resolver::{FsPath, FsResolver, AT_FDCWD},
|
||||||
time::{ClockID, SystemTime, ALL_SUPPORTED_CLOCK_IDS},
|
time::{ClockID, SystemTime, START_TIME},
|
||||||
vm::vmo::{Vmo, VmoOptions},
|
vm::vmo::{Vmo, VmoOptions},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -46,20 +48,25 @@ enum VdsoClockMode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Instant used in `VdsoData`.
|
/// Instant used in `VdsoData`.
|
||||||
/// The `VdsoInstant` records the second of an instant,
|
///
|
||||||
/// and the calculation results of multiplying `nanos` with `mult` in the corresponding `VdsoData`.
|
/// Each `VdsoInstant` will store a instant information for a specified `ClockID`.
|
||||||
|
/// The `secs` field will record the seconds of the instant,
|
||||||
|
/// and the `nanos_info` will store the nanoseconds of the instant
|
||||||
|
/// (for `CLOCK_REALTIME_COARSE` and `CLOCK_MONOTONIC_COARSE`) or
|
||||||
|
/// the calculation results of left-shift `nanos` with `lshift`
|
||||||
|
/// (for other high-resolution `ClockID`s).
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Default, Copy, Clone, Pod)]
|
#[derive(Debug, Default, Copy, Clone, Pod)]
|
||||||
struct VdsoInstant {
|
struct VdsoInstant {
|
||||||
secs: u64,
|
secs: u64,
|
||||||
nanos_lshift: u64,
|
nanos_info: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VdsoInstant {
|
impl VdsoInstant {
|
||||||
const fn zero() -> Self {
|
const fn zero() -> Self {
|
||||||
Self {
|
Self {
|
||||||
secs: 0,
|
secs: 0,
|
||||||
nanos_lshift: 0,
|
nanos_info: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -70,7 +77,7 @@ struct ArchVdsoData {}
|
|||||||
|
|
||||||
/// A POD (Plain Old Data) structure maintaining timing information that required for userspace.
|
/// A POD (Plain Old Data) structure maintaining timing information that required for userspace.
|
||||||
///
|
///
|
||||||
/// Since currently we directly use the vdso shared library of Linux,
|
/// Since currently we directly use the VDSO shared library of Linux,
|
||||||
/// currently it aligns with the Linux VDSO shared library format and contents
|
/// currently it aligns with the Linux VDSO shared library format and contents
|
||||||
/// (Linux v6.2.10)
|
/// (Linux v6.2.10)
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
@ -93,6 +100,18 @@ struct VdsoData {
|
|||||||
arch_data: ArchVdsoData,
|
arch_data: ArchVdsoData,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const HIGH_RES_CLOCK_IDS: [ClockID; 4] = [
|
||||||
|
ClockID::CLOCK_REALTIME,
|
||||||
|
ClockID::CLOCK_MONOTONIC,
|
||||||
|
ClockID::CLOCK_MONOTONIC_RAW,
|
||||||
|
ClockID::CLOCK_BOOTTIME,
|
||||||
|
];
|
||||||
|
|
||||||
|
const COARSE_RES_CLOCK_IDS: [ClockID; 2] = [
|
||||||
|
ClockID::CLOCK_REALTIME_COARSE,
|
||||||
|
ClockID::CLOCK_MONOTONIC_COARSE,
|
||||||
|
];
|
||||||
|
|
||||||
impl VdsoData {
|
impl VdsoData {
|
||||||
const fn empty() -> Self {
|
const fn empty() -> Self {
|
||||||
VdsoData {
|
VdsoData {
|
||||||
@ -111,13 +130,16 @@ impl VdsoData {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Init vdso data based on the default clocksource.
|
/// Init VDSO data based on the default clocksource.
|
||||||
fn init(&mut self) {
|
fn init(&mut self) {
|
||||||
let clocksource = aster_time::default_clocksource();
|
let clocksource = aster_time::default_clocksource();
|
||||||
let coeff = clocksource.coeff();
|
let coeff = clocksource.coeff();
|
||||||
self.set_clock_mode(DEFAULT_CLOCK_MODE);
|
self.set_clock_mode(DEFAULT_CLOCK_MODE);
|
||||||
self.set_coeff(coeff);
|
self.set_coeff(coeff);
|
||||||
self.update_instant(clocksource.last_instant(), clocksource.last_cycles());
|
|
||||||
|
let (last_instant, last_cycles) = clocksource.last_record();
|
||||||
|
self.update_high_res_instant(last_instant, last_cycles);
|
||||||
|
self.update_coarse_res_instant(last_instant);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_clock_mode(&mut self, mode: VdsoClockMode) {
|
fn set_clock_mode(&mut self, mode: VdsoClockMode) {
|
||||||
@ -129,21 +151,20 @@ impl VdsoData {
|
|||||||
self.shift = coeff.shift();
|
self.shift = coeff.shift();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_clock_instant(&mut self, clockid: usize, secs: u64, nanos_lshift: u64) {
|
fn update_clock_instant(&mut self, clockid: usize, secs: u64, nanos_info: u64) {
|
||||||
self.basetime[clockid].secs = secs;
|
self.basetime[clockid].secs = secs;
|
||||||
self.basetime[clockid].nanos_lshift = nanos_lshift;
|
self.basetime[clockid].nanos_info = nanos_info;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_instant(&mut self, instant: Instant, instant_cycles: u64) {
|
fn update_high_res_instant(&mut self, instant: Instant, instant_cycles: u64) {
|
||||||
self.last_cycles = instant_cycles;
|
self.last_cycles = instant_cycles;
|
||||||
const REALTIME_IDS: [ClockID; 2] =
|
for clock_id in HIGH_RES_CLOCK_IDS {
|
||||||
[ClockID::CLOCK_REALTIME, ClockID::CLOCK_REALTIME_COARSE];
|
let secs = if clock_id == ClockID::CLOCK_REALTIME {
|
||||||
for clock_id in ALL_SUPPORTED_CLOCK_IDS {
|
|
||||||
let secs = if REALTIME_IDS.contains(&clock_id) {
|
|
||||||
instant.secs() + START_SECS_COUNT.get().unwrap()
|
instant.secs() + START_SECS_COUNT.get().unwrap()
|
||||||
} else {
|
} else {
|
||||||
instant.secs()
|
instant.secs()
|
||||||
};
|
};
|
||||||
|
|
||||||
self.update_clock_instant(
|
self.update_clock_instant(
|
||||||
clock_id as usize,
|
clock_id as usize,
|
||||||
secs,
|
secs,
|
||||||
@ -151,31 +172,48 @@ impl VdsoData {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn update_coarse_res_instant(&mut self, instant: Instant) {
|
||||||
|
for clock_id in COARSE_RES_CLOCK_IDS {
|
||||||
|
let secs = if clock_id == ClockID::CLOCK_REALTIME_COARSE {
|
||||||
|
instant.secs() + START_SECS_COUNT.get().unwrap()
|
||||||
|
} else {
|
||||||
|
instant.secs()
|
||||||
|
};
|
||||||
|
self.update_clock_instant(clock_id as usize, secs, instant.nanos() as u64);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Vdso (virtual dynamic shared object) is used to export some safe kernel space routines to user space applications
|
/// Vdso (virtual dynamic shared object) is used to export some safe kernel space routines to user space applications
|
||||||
/// so that applications can call these kernel space routines in-process, without context switching.
|
/// so that applications can call these kernel space routines in-process, without context switching.
|
||||||
///
|
///
|
||||||
/// Vdso maintains a `VdsoData` instance that contains data information required for vdso mechanism,
|
/// Vdso maintains a `VdsoData` instance that contains data information required for VDSO mechanism,
|
||||||
/// and a `Vmo` that contains all vdso-related information, including the vdso data and the vdso calling interfaces.
|
/// and a `Vmo` that contains all VDSO-related information, including the VDSO data and the VDSO calling interfaces.
|
||||||
/// This `Vmo` must be mapped to every userspace process.
|
/// This `Vmo` must be mapped to every userspace process.
|
||||||
struct Vdso {
|
struct Vdso {
|
||||||
/// A VdsoData instance.
|
/// A VdsoData instance.
|
||||||
data: Mutex<VdsoData>,
|
data: SpinLock<VdsoData>,
|
||||||
/// the vmo of the entire vdso, including the library text and the vdso data.
|
/// The vmo of the entire VDSO, including the library text and the VDSO data.
|
||||||
vmo: Arc<Vmo>,
|
vmo: Arc<Vmo>,
|
||||||
|
/// The `VmFrame` that contains the VDSO data. This frame is contained in and
|
||||||
|
/// will not be removed from the VDSO vmo.
|
||||||
|
data_frame: VmFrame,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A `SpinLock` for the `seq` field in `VdsoData`.
|
||||||
|
static SEQ_LOCK: SpinLock<()> = SpinLock::new(());
|
||||||
|
|
||||||
impl Vdso {
|
impl Vdso {
|
||||||
/// Construct a new Vdso, including an initialized `VdsoData` and a vmo of the vdso.
|
/// Construct a new Vdso, including an initialized `VdsoData` and a vmo of the VDSO.
|
||||||
fn new() -> Self {
|
fn new() -> Self {
|
||||||
let mut vdso_data = VdsoData::empty();
|
let mut vdso_data = VdsoData::empty();
|
||||||
vdso_data.init();
|
vdso_data.init();
|
||||||
|
|
||||||
let vdso_vmo = {
|
let (vdso_vmo, data_frame) = {
|
||||||
let vmo_options = VmoOptions::<Rights>::new(5 * PAGE_SIZE);
|
let vmo_options = VmoOptions::<Rights>::new(5 * PAGE_SIZE);
|
||||||
let vdso_vmo = vmo_options.alloc().unwrap();
|
let vdso_vmo = vmo_options.alloc().unwrap();
|
||||||
// Write vdso data to vdso vmo.
|
// Write VDSO data to VDSO vmo.
|
||||||
vdso_vmo.write_bytes(0x80, vdso_data.as_bytes()).unwrap();
|
vdso_vmo.write_bytes(0x80, vdso_data.as_bytes()).unwrap();
|
||||||
|
|
||||||
let vdso_lib_vmo = {
|
let vdso_lib_vmo = {
|
||||||
@ -186,62 +224,87 @@ impl Vdso {
|
|||||||
};
|
};
|
||||||
let mut vdso_text = Box::new([0u8; PAGE_SIZE]);
|
let mut vdso_text = Box::new([0u8; PAGE_SIZE]);
|
||||||
vdso_lib_vmo.read_bytes(0, &mut *vdso_text).unwrap();
|
vdso_lib_vmo.read_bytes(0, &mut *vdso_text).unwrap();
|
||||||
// Write vdso library to vdso vmo.
|
// Write VDSO library to VDSO vmo.
|
||||||
vdso_vmo.write_bytes(0x4000, &*vdso_text).unwrap();
|
vdso_vmo.write_bytes(0x4000, &*vdso_text).unwrap();
|
||||||
|
|
||||||
vdso_vmo
|
let data_frame = vdso_vmo.get_committed_frame(0, true).unwrap();
|
||||||
|
(vdso_vmo, data_frame)
|
||||||
};
|
};
|
||||||
Self {
|
Self {
|
||||||
data: Mutex::new(vdso_data),
|
data: SpinLock::new(vdso_data),
|
||||||
vmo: Arc::new(vdso_vmo),
|
vmo: Arc::new(vdso_vmo),
|
||||||
|
data_frame,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the vdso vmo.
|
fn update_high_res_instant(&self, instant: Instant, instant_cycles: u64) {
|
||||||
fn vmo(&self) -> Arc<Vmo> {
|
let seq_lock = SEQ_LOCK.lock();
|
||||||
self.vmo.clone()
|
self.data
|
||||||
}
|
.lock()
|
||||||
|
.update_high_res_instant(instant, instant_cycles);
|
||||||
fn update_instant(&self, instant: Instant, instant_cycles: u64) {
|
|
||||||
self.data.lock().update_instant(instant, instant_cycles);
|
|
||||||
|
|
||||||
// Update begins.
|
// Update begins.
|
||||||
self.vmo.write_val(0x80, &1).unwrap();
|
self.data_frame.write_val(0x80, &1).unwrap();
|
||||||
self.vmo.write_val(0x88, &instant_cycles).unwrap();
|
self.data_frame.write_val(0x88, &instant_cycles).unwrap();
|
||||||
for clock_id in ALL_SUPPORTED_CLOCK_IDS {
|
for clock_id in HIGH_RES_CLOCK_IDS {
|
||||||
self.update_vmo_instant(clock_id);
|
self.update_data_frame_instant(clock_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update finishes.
|
// Update finishes.
|
||||||
self.vmo.write_val(0x80, &0).unwrap();
|
self.data_frame.write_val(0x80, &0).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update the requisite fields of the vdso data in the vmo.
|
fn update_coarse_res_instant(&self, instant: Instant) {
|
||||||
fn update_vmo_instant(&self, clockid: ClockID) {
|
let seq_lock = SEQ_LOCK.lock();
|
||||||
|
self.data.lock().update_coarse_res_instant(instant);
|
||||||
|
|
||||||
|
// Update begins.
|
||||||
|
self.data_frame.write_val(0x80, &1).unwrap();
|
||||||
|
for clock_id in COARSE_RES_CLOCK_IDS {
|
||||||
|
self.update_data_frame_instant(clock_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update finishes.
|
||||||
|
self.data_frame.write_val(0x80, &0).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Update the requisite fields of the VDSO data in the `data_frame`.
|
||||||
|
fn update_data_frame_instant(&self, clockid: ClockID) {
|
||||||
let clock_index = clockid as usize;
|
let clock_index = clockid as usize;
|
||||||
let secs_offset = 0xA0 + clock_index * 0x10;
|
let secs_offset = 0xA0 + clock_index * 0x10;
|
||||||
let nanos_lshift_offset = 0xA8 + clock_index * 0x10;
|
let nanos_info_offset = 0xA8 + clock_index * 0x10;
|
||||||
let data = self.data.lock();
|
let data = self.data.lock();
|
||||||
self.vmo
|
self.data_frame
|
||||||
.write_val(secs_offset, &data.basetime[clock_index].secs)
|
.write_val(secs_offset, &data.basetime[clock_index].secs)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
self.vmo
|
self.data_frame
|
||||||
.write_val(
|
.write_val(nanos_info_offset, &data.basetime[clock_index].nanos_info)
|
||||||
nanos_lshift_offset,
|
|
||||||
&data.basetime[clock_index].nanos_lshift,
|
|
||||||
)
|
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update the `VdsoInstant` in Vdso.
|
/// Update the `VdsoInstant` for clock IDs with high resolution in Vdso.
|
||||||
fn update_vdso_instant(instant: Instant, instant_cycles: u64) {
|
fn update_vdso_high_res_instant(instant: Instant, instant_cycles: u64) {
|
||||||
VDSO.get().unwrap().update_instant(instant, instant_cycles);
|
VDSO.get()
|
||||||
|
.unwrap()
|
||||||
|
.update_high_res_instant(instant, instant_cycles);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Update the `VdsoInstant` for clock IDs with coarse resolution in Vdso.
|
||||||
|
fn update_vdso_coarse_res_instant(timer: Arc<Timer>) {
|
||||||
|
let instant = Instant::from(read_monotonic_time());
|
||||||
|
VDSO.get().unwrap().update_coarse_res_instant(instant);
|
||||||
|
|
||||||
|
timer.set(Duration::from_millis(100));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Init `START_SECS_COUNT`, which is used to record the seconds passed since 1970-01-01 00:00:00.
|
/// Init `START_SECS_COUNT`, which is used to record the seconds passed since 1970-01-01 00:00:00.
|
||||||
fn init_start_secs_count() {
|
fn init_start_secs_count() {
|
||||||
let now = SystemTime::now();
|
let time_duration = START_TIME
|
||||||
let time_duration = now.duration_since(&SystemTime::UNIX_EPOCH).unwrap();
|
.get()
|
||||||
|
.unwrap()
|
||||||
|
.duration_since(&SystemTime::UNIX_EPOCH)
|
||||||
|
.unwrap();
|
||||||
START_SECS_COUNT.call_once(|| time_duration.as_secs());
|
START_SECS_COUNT.call_once(|| time_duration.as_secs());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -250,15 +313,20 @@ fn init_vdso() {
|
|||||||
VDSO.call_once(|| Arc::new(vdso));
|
VDSO.call_once(|| Arc::new(vdso));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Init vdso module.
|
/// Init this module.
|
||||||
pub(super) fn init() {
|
pub(super) fn init() {
|
||||||
init_start_secs_count();
|
init_start_secs_count();
|
||||||
init_vdso();
|
init_vdso();
|
||||||
aster_time::VDSO_DATA_UPDATE.call_once(|| Arc::new(update_vdso_instant));
|
aster_time::VDSO_DATA_HIGH_RES_UPDATE_FN.call_once(|| Arc::new(update_vdso_high_res_instant));
|
||||||
|
|
||||||
|
// Coarse resolution clock IDs directly read the instant stored in VDSO data without
|
||||||
|
// using coefficients for calculation, thus the related instant requires more frequent updating.
|
||||||
|
let coarse_instant_timer = Timer::new(update_vdso_coarse_res_instant).unwrap();
|
||||||
|
coarse_instant_timer.set(Duration::from_millis(100));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the vdso vmo.
|
/// Return the VDSO vmo.
|
||||||
pub(crate) fn vdso_vmo() -> Option<Arc<Vmo>> {
|
pub(crate) fn vdso_vmo() -> Option<Arc<Vmo>> {
|
||||||
// We allow that vdso does not exist
|
// We allow that VDSO does not exist
|
||||||
VDSO.get().map(|vdso| vdso.vmo())
|
VDSO.get().map(|vdso| vdso.vmo.clone())
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,19 @@
|
|||||||
// SPDX-License-Identifier: MPL-2.0
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
//! This module provides abstractions for hardware-assisted timing mechanisms, encapsulated by the `ClockSource` struct.
|
//! This module provides abstractions for hardware-assisted timing mechanisms, encapsulated
|
||||||
//! A `ClockSource` can be constructed from any counter with a stable frequency, enabling precise time measurements to be taken
|
//! by the `ClockSource` struct.
|
||||||
//! by retrieving instances of `Instant`.
|
|
||||||
//!
|
//!
|
||||||
//! The `ClockSource` module is a fundamental building block for timing in systems that require high precision and accuracy.
|
//! A `ClockSource` can be constructed from any counter with a stable frequency, enabling precise
|
||||||
//! It can be integrated into larger systems to provide timing capabilities, or used standalone for time tracking and elapsed time measurements.
|
//! time measurements to be taken by retrieving instances of `Instant`.
|
||||||
|
//!
|
||||||
|
//! The `ClockSource` module is a fundamental building block for timing in systems that require
|
||||||
|
//! high precision and accuracy. It can be integrated into larger systems to provide timing capabilities,
|
||||||
|
//! or used standalone for time tracking and elapsed time measurements.
|
||||||
|
|
||||||
use alloc::sync::Arc;
|
use alloc::sync::Arc;
|
||||||
use core::{cmp::max, ops::Add, time::Duration};
|
use core::{cmp::max, ops::Add, time::Duration};
|
||||||
|
|
||||||
use aster_frame::sync::SpinLock;
|
use aster_frame::sync::RwLock;
|
||||||
use aster_util::coeff::Coeff;
|
use aster_util::coeff::Coeff;
|
||||||
|
|
||||||
use crate::NANOS_PER_SECOND;
|
use crate::NANOS_PER_SECOND;
|
||||||
@ -20,15 +23,21 @@ use crate::NANOS_PER_SECOND;
|
|||||||
/// Users are able to measure time by retrieving `Instant` from this source.
|
/// Users are able to measure time by retrieving `Instant` from this source.
|
||||||
///
|
///
|
||||||
/// # Implementation
|
/// # Implementation
|
||||||
/// The `ClockSource` relies on obtaining the frequency of the counter and the method for reading the cycles in order to measure time.
|
/// The `ClockSource` relies on obtaining the frequency of the counter and the method for
|
||||||
|
/// reading the cycles in order to measure time.
|
||||||
|
///
|
||||||
/// The **cycles** here refer the counts of the base time counter.
|
/// The **cycles** here refer the counts of the base time counter.
|
||||||
/// Additionally, the `ClockSource` also holds a last recorded instant, which acts as a reference point for subsequent time retrieval.
|
///
|
||||||
/// To prevent numerical overflow during the calculation of `Instant`, this last recorded instant **must be periodically refreshed**.
|
/// Additionally, the `ClockSource` also holds a last record for an `Instant` and the
|
||||||
/// The maximum interval for these updates must be determined at the time of the `ClockSource` initialization.
|
/// corresponding cycles, which acts as a reference point for subsequent time retrieval.
|
||||||
|
/// To prevent numerical overflow during the calculation of `Instant`, this last recorded instant
|
||||||
|
/// **must be periodically refreshed**. The maximum interval for these updates must be determined
|
||||||
|
/// at the time of the `ClockSource` initialization.
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
/// Suppose we have a counter called `counter` which have the frequency `counter.freq`, and the method to read its cycles called `read_counter()`.
|
/// Suppose we have a counter called `counter` which have the frequency `counter.freq`, and the method
|
||||||
/// We can create a corresponding `ClockSource` and use it as follows:
|
/// to read its cycles called `read_counter()`. We can create a corresponding `ClockSource` and
|
||||||
|
/// use it as follows:
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// // here we set the max_delay_secs = 10
|
/// // here we set the max_delay_secs = 10
|
||||||
@ -45,16 +54,17 @@ pub struct ClockSource {
|
|||||||
read_cycles: Arc<dyn Fn() -> u64 + Sync + Send>,
|
read_cycles: Arc<dyn Fn() -> u64 + Sync + Send>,
|
||||||
base: ClockSourceBase,
|
base: ClockSourceBase,
|
||||||
coeff: Coeff,
|
coeff: Coeff,
|
||||||
last_instant: SpinLock<Instant>,
|
/// A record to an `Instant` and the corresponding cycles of this `ClockSource`.
|
||||||
last_cycles: SpinLock<u64>,
|
last_record: RwLock<(Instant, u64)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ClockSource {
|
impl ClockSource {
|
||||||
/// Create a new `ClockSource` instance.
|
/// Create a new `ClockSource` instance.
|
||||||
/// Require basic information of based time counter, including the function for reading cycles, the frequency
|
/// Require basic information of based time counter, including the function for reading cycles,
|
||||||
/// and the maximum delay seconds to update this `ClockSource`.
|
/// the frequency and the maximum delay seconds to update this `ClockSource`.
|
||||||
/// The `ClockSource` also calculates a reliable `Coeff` based on the counter's frequency and the maximum delay seconds.
|
/// The `ClockSource` also calculates a reliable `Coeff` based on the counter's frequency and
|
||||||
/// This `Coeff` is used to convert the number of cycles into the duration of time that has passed for those cycles.
|
/// the maximum delay seconds. This `Coeff` is used to convert the number of cycles into
|
||||||
|
/// the duration of time that has passed for those cycles.
|
||||||
pub fn new(
|
pub fn new(
|
||||||
freq: u64,
|
freq: u64,
|
||||||
max_delay_secs: u64,
|
max_delay_secs: u64,
|
||||||
@ -68,8 +78,7 @@ impl ClockSource {
|
|||||||
read_cycles,
|
read_cycles,
|
||||||
base,
|
base,
|
||||||
coeff,
|
coeff,
|
||||||
last_instant: SpinLock::new(Instant::zero()),
|
last_record: RwLock::new((Instant::zero(), 0)),
|
||||||
last_cycles: SpinLock::new(0),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,25 +87,23 @@ impl ClockSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Use the instant cycles to calculate the instant.
|
/// Use the instant cycles to calculate the instant.
|
||||||
/// It first calculates the difference between the instant cycles and the last recorded cycles stored in the clocksource.
|
///
|
||||||
/// Then `ClockSource` will convert the passed cycles into passed time and calculate the current instant.
|
/// It first calculates the difference between the instant cycles and the last
|
||||||
|
/// recorded cycles stored in the clocksource. Then `ClockSource` will convert
|
||||||
|
/// the passed cycles into passed time and calculate the current instant.
|
||||||
fn calculate_instant(&self, instant_cycles: u64) -> Instant {
|
fn calculate_instant(&self, instant_cycles: u64) -> Instant {
|
||||||
|
let (last_instant, last_cycles) = *self.last_record.read_irq_disabled();
|
||||||
let delta_nanos = {
|
let delta_nanos = {
|
||||||
let delta_cycles = instant_cycles - self.last_cycles();
|
let delta_cycles = instant_cycles - last_cycles;
|
||||||
self.cycles_to_nanos(delta_cycles)
|
self.cycles_to_nanos(delta_cycles)
|
||||||
};
|
};
|
||||||
let duration = Duration::from_nanos(delta_nanos);
|
let duration = Duration::from_nanos(delta_nanos);
|
||||||
self.last_instant() + duration
|
last_instant + duration
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Use an input instant to update the internal instant in the `ClockSource`.
|
/// Use an input instant and cycles to update the `last_record` in the `ClockSource`.
|
||||||
fn update_last_instant(&self, instant: &Instant) {
|
fn update_last_record(&self, record: (Instant, u64)) {
|
||||||
*self.last_instant.lock() = *instant;
|
*self.last_record.write_irq_disabled() = record;
|
||||||
}
|
|
||||||
|
|
||||||
/// Use an input cycles to update the internal instant in the `ClockSource`.
|
|
||||||
fn update_last_cycles(&self, cycles: u64) {
|
|
||||||
*self.last_cycles.lock() = cycles;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// read current cycles of the `ClockSource`.
|
/// read current cycles of the `ClockSource`.
|
||||||
@ -104,19 +111,14 @@ impl ClockSource {
|
|||||||
(self.read_cycles)()
|
(self.read_cycles)()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the last instant recorded in the `ClockSource`.
|
/// Return the last instant and last cycles recorded in the `ClockSource`.
|
||||||
pub fn last_instant(&self) -> Instant {
|
pub fn last_record(&self) -> (Instant, u64) {
|
||||||
return *self.last_instant.lock();
|
return *self.last_record.read_irq_disabled();
|
||||||
}
|
|
||||||
|
|
||||||
/// Return the last cycles recorded in the `ClockSource`.
|
|
||||||
pub fn last_cycles(&self) -> u64 {
|
|
||||||
return *self.last_cycles.lock();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the maximum delay seconds for updating of the `ClockSource`.
|
/// Return the maximum delay seconds for updating of the `ClockSource`.
|
||||||
pub fn max_delay_secs(&self) -> u64 {
|
pub fn max_delay_secs(&self) -> u64 {
|
||||||
self.base.max_delay_secs()
|
self.base.max_delay_secs
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the reference to the generated cycles coeff of the `ClockSource`.
|
/// Return the reference to the generated cycles coeff of the `ClockSource`.
|
||||||
@ -126,26 +128,24 @@ impl ClockSource {
|
|||||||
|
|
||||||
/// Return the frequency of the counter used in the `ClockSource`.
|
/// Return the frequency of the counter used in the `ClockSource`.
|
||||||
pub fn freq(&self) -> u64 {
|
pub fn freq(&self) -> u64 {
|
||||||
self.base.freq()
|
self.base.freq
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calibrate the recorded `Instant` to zero, and record the instant cycles.
|
/// Calibrate the recorded `Instant` to zero, and record the instant cycles.
|
||||||
pub(crate) fn calibrate(&self, instant_cycles: u64) {
|
pub(crate) fn calibrate(&self, instant_cycles: u64) {
|
||||||
self.update_last_cycles(instant_cycles);
|
self.update_last_record((Instant::zero(), instant_cycles));
|
||||||
self.update_last_instant(&Instant::zero());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the instant to update the internal instant in the `ClockSource`.
|
/// Get the instant to update the internal instant in the `ClockSource`.
|
||||||
pub(crate) fn update(&self) {
|
pub(crate) fn update(&self) {
|
||||||
let instant_cycles = self.read_cycles();
|
let instant_cycles = self.read_cycles();
|
||||||
let instant = self.calculate_instant(instant_cycles);
|
let instant = self.calculate_instant(instant_cycles);
|
||||||
self.update_last_cycles(instant_cycles);
|
self.update_last_record((instant, instant_cycles));
|
||||||
self.update_last_instant(&instant);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Read the instant corresponding to the current time.
|
/// Read the instant corresponding to the current time.
|
||||||
/// When trying to read an instant from the clocksource, it will use the reading method to read instant cycles.
|
/// When trying to read an instant from the clocksource, it will use the reading method
|
||||||
/// Then leverage it to calculate the instant.
|
/// to read instant cycles. Then leverage it to calculate the instant.
|
||||||
pub(crate) fn read_instant(&self) -> Instant {
|
pub(crate) fn read_instant(&self) -> Instant {
|
||||||
let instant_cycles = self.read_cycles();
|
let instant_cycles = self.read_cycles();
|
||||||
self.calculate_instant(instant_cycles)
|
self.calculate_instant(instant_cycles)
|
||||||
@ -162,10 +162,12 @@ pub struct Instant {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Instant {
|
impl Instant {
|
||||||
|
/// Create a zeroed `Instant`.
|
||||||
pub const fn zero() -> Self {
|
pub const fn zero() -> Self {
|
||||||
Self { secs: 0, nanos: 0 }
|
Self { secs: 0, nanos: 0 }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create an new `Instant` based on the inputting `secs` and `nanos`.
|
||||||
pub fn new(secs: u64, nanos: u32) -> Self {
|
pub fn new(secs: u64, nanos: u32) -> Self {
|
||||||
Self { secs, nanos }
|
Self { secs, nanos }
|
||||||
}
|
}
|
||||||
@ -181,6 +183,15 @@ impl Instant {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<Duration> for Instant {
|
||||||
|
fn from(value: Duration) -> Self {
|
||||||
|
Self {
|
||||||
|
secs: value.as_secs(),
|
||||||
|
nanos: value.subsec_nanos(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Add<Duration> for Instant {
|
impl Add<Duration> for Instant {
|
||||||
type Output = Instant;
|
type Output = Instant;
|
||||||
|
|
||||||
@ -210,12 +221,4 @@ impl ClockSourceBase {
|
|||||||
max_delay_secs,
|
max_delay_secs,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn max_delay_secs(&self) -> u64 {
|
|
||||||
self.max_delay_secs
|
|
||||||
}
|
|
||||||
|
|
||||||
fn freq(&self) -> u64 {
|
|
||||||
self.freq
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,8 @@ mod rtc;
|
|||||||
mod tsc;
|
mod tsc;
|
||||||
|
|
||||||
pub const NANOS_PER_SECOND: u32 = 1_000_000_000;
|
pub const NANOS_PER_SECOND: u32 = 1_000_000_000;
|
||||||
pub static VDSO_DATA_UPDATE: Once<Arc<dyn Fn(Instant, u64) + Sync + Send>> = Once::new();
|
pub static VDSO_DATA_HIGH_RES_UPDATE_FN: Once<Arc<dyn Fn(Instant, u64) + Sync + Send>> =
|
||||||
|
Once::new();
|
||||||
|
|
||||||
#[init_component]
|
#[init_component]
|
||||||
fn time_init() -> Result<(), ComponentInitError> {
|
fn time_init() -> Result<(), ComponentInitError> {
|
||||||
|
@ -14,7 +14,7 @@ use spin::Once;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
clocksource::{ClockSource, Instant},
|
clocksource::{ClockSource, Instant},
|
||||||
START_TIME, VDSO_DATA_UPDATE,
|
START_TIME, VDSO_DATA_HIGH_RES_UPDATE_FN,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A instance of TSC clocksource.
|
/// A instance of TSC clocksource.
|
||||||
@ -58,8 +58,9 @@ fn update_clocksource(timer: Arc<Timer>) {
|
|||||||
clock.update();
|
clock.update();
|
||||||
|
|
||||||
// Update vdso data.
|
// Update vdso data.
|
||||||
if VDSO_DATA_UPDATE.is_completed() {
|
if let Some(update_fn) = VDSO_DATA_HIGH_RES_UPDATE_FN.get() {
|
||||||
VDSO_DATA_UPDATE.get().unwrap()(clock.last_instant(), clock.last_cycles());
|
let (last_instant, last_cycles) = clock.last_record();
|
||||||
|
update_fn(last_instant, last_cycles);
|
||||||
}
|
}
|
||||||
// Setting the timer as `clock.max_delay_secs() - 1` is to avoid
|
// Setting the timer as `clock.max_delay_secs() - 1` is to avoid
|
||||||
// the actual delay time is greater than the maximum delay seconds due to the latency of execution.
|
// the actual delay time is greater than the maximum delay seconds due to the latency of execution.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user