Implement a high precision gettime based on tsc

This commit is contained in:
Chen Chengjun
2023-12-06 15:03:42 +08:00
committed by Tate, Hongliang Tian
parent ba08895fc3
commit 715072b9f3
14 changed files with 618 additions and 89 deletions

2
Cargo.lock generated
View File

@ -804,6 +804,7 @@ version = "0.1.0"
dependencies = [ dependencies = [
"component", "component",
"jinux-frame", "jinux-frame",
"jinux-util",
"log", "log",
"spin 0.9.8", "spin 0.9.8",
] ]
@ -816,6 +817,7 @@ dependencies = [
"jinux-frame", "jinux-frame",
"jinux-rights", "jinux-rights",
"jinux-rights-proc", "jinux-rights-proc",
"ktest",
"pod", "pod",
"typeflags-util", "typeflags-util",
] ]

View File

@ -1,11 +1,23 @@
use core::sync::atomic::AtomicU64;
use x86::cpuid::cpuid; use x86::cpuid::cpuid;
/// The frequency of tsc. The unit is Hz.
pub(crate) static TSC_FREQ: AtomicU64 = AtomicU64::new(0);
const TSC_DEADLINE_MODE_SUPPORT: u32 = 1 << 24;
/// Determine if the current system supports tsc_deadline mode.
pub fn is_tsc_deadline_mode_supported() -> bool {
let cpuid = cpuid!(1);
(cpuid.ecx & TSC_DEADLINE_MODE_SUPPORT) > 0
}
/// Determine TSC frequency via CPUID. If the CPU does not support calculating TSC frequency by /// Determine TSC frequency via CPUID. If the CPU does not support calculating TSC frequency by
/// CPUID, the function will return None. The unit of the return value is KHz. /// CPUID, the function will return None. The unit of the return value is KHz.
/// ///
/// Ref: function `native_calibrate_tsc` in linux `arch/x86/kernel/tsc.c` /// Ref: function `native_calibrate_tsc` in linux `arch/x86/kernel/tsc.c`
/// ///
pub fn tsc_freq() -> Option<u32> { pub fn determine_tsc_freq_via_cpuid() -> Option<u32> {
// Check the max cpuid supported // Check the max cpuid supported
let cpuid = cpuid!(0); let cpuid = cpuid!(0);
let max_cpuid = cpuid.eax; let max_cpuid = cpuid.eax;

View File

@ -12,6 +12,8 @@ pub mod qemu;
pub(crate) mod tdx_guest; pub(crate) mod tdx_guest;
pub(crate) mod timer; pub(crate) mod timer;
use core::{arch::x86_64::_rdtsc, sync::atomic::Ordering};
use kernel::apic::ioapic; use kernel::apic::ioapic;
use log::{info, warn}; use log::{info, warn};
@ -50,6 +52,17 @@ pub(crate) fn interrupts_ack() {
} }
} }
/// Return the frequency of TSC. The unit is Hz.
pub fn tsc_freq() -> u64 {
kernel::tsc::TSC_FREQ.load(Ordering::Acquire)
}
/// Reads the current value of the processors time-stamp counter (TSC).
pub fn read_tsc() -> u64 {
// Safety: It is safe to read a time-related counter.
unsafe { _rdtsc() }
}
fn enable_common_cpu_features() { fn enable_common_cpu_features() {
use x86_64::registers::{control::Cr4Flags, model_specific::EferFlags, xcontrol::XCr0Flags}; use x86_64::registers::{control::Cr4Flags, model_specific::EferFlags, xcontrol::XCr0Flags};
let mut cr4 = x86_64::registers::control::Cr4::read(); let mut cr4 = x86_64::registers::control::Cr4::read();

View File

@ -1,55 +1,45 @@
use core::arch::x86_64::_rdtsc;
use core::sync::atomic::{AtomicBool, Ordering};
use alloc::sync::Arc; use alloc::sync::Arc;
use core::arch::x86_64::_rdtsc;
use core::sync::atomic::{AtomicBool, AtomicU64, Ordering};
use log::info; use log::info;
use spin::Once; use spin::Once;
use trapframe::TrapFrame; use trapframe::TrapFrame;
use x86::cpuid::cpuid;
use x86::msr::{wrmsr, IA32_TSC_DEADLINE}; use x86::msr::{wrmsr, IA32_TSC_DEADLINE};
use crate::arch::kernel::apic::ioapic::IO_APIC; use crate::arch::kernel::apic::ioapic::IO_APIC;
use crate::arch::kernel::tsc::is_tsc_deadline_mode_supported;
use crate::arch::x86::kernel::apic::{DivideConfig, APIC_INSTANCE};
use crate::arch::x86::kernel::tsc::{determine_tsc_freq_via_cpuid, TSC_FREQ};
use crate::config::TIMER_FREQ;
use crate::trap::IrqLine; use crate::trap::IrqLine;
use crate::{
arch::x86::kernel::{
apic::{DivideConfig, APIC_INSTANCE},
tsc::tsc_freq,
},
config::TIMER_FREQ,
};
pub fn init() { pub fn init() {
if tsc_mode_support() { init_tsc_freq();
info!("[Timer]: Enable APIC-TSC deadline mode."); if is_tsc_deadline_mode_supported() {
tsc_mode_init(); info!("[Timer]: Enable APIC TSC deadline mode.");
init_tsc_mode();
} else { } else {
info!("[Timer]: Enable APIC-periodic mode."); info!("[Timer]: Enable APIC periodic mode.");
periodic_mode_init(); init_periodic_mode();
} }
} }
fn tsc_mode_support() -> bool {
let tsc_rate = tsc_freq();
if tsc_rate.is_none() {
return false;
}
let cpuid = cpuid!(0x1);
// bit 24
cpuid.ecx & 0x100_0000 != 0
}
pub(super) static APIC_TIMER_CALLBACK: Once<Arc<dyn Fn() + Sync + Send>> = Once::new(); pub(super) static APIC_TIMER_CALLBACK: Once<Arc<dyn Fn() + Sync + Send>> = Once::new();
fn tsc_mode_init() { fn init_tsc_freq() {
let tsc_freq = determine_tsc_freq_via_cpuid()
.map_or(determine_tsc_freq_via_pit(), |freq| freq as u64 * 1000);
TSC_FREQ.store(tsc_freq, Ordering::Relaxed);
info!("TSC frequency:{:?} Hz", tsc_freq);
}
fn init_tsc_mode() {
let mut apic_lock = APIC_INSTANCE.get().unwrap().lock(); let mut apic_lock = APIC_INSTANCE.get().unwrap().lock();
// Enable tsc deadline mode. // Enable tsc deadline mode.
apic_lock.set_lvt_timer(super::TIMER_IRQ_NUM.load(Ordering::Relaxed) as u64 | (1 << 18)); apic_lock.set_lvt_timer(super::TIMER_IRQ_NUM.load(Ordering::Relaxed) as u64 | (1 << 18));
drop(apic_lock);
let tsc_step = { let tsc_step = TSC_FREQ.load(Ordering::Relaxed) / TIMER_FREQ;
let tsc_rate = tsc_freq().unwrap() as u64;
info!("TSC frequency:{:?} Hz", tsc_rate * 1000);
tsc_rate * 1000 / TIMER_FREQ
};
let callback = move || unsafe { let callback = move || unsafe {
let tsc_value = _rdtsc(); let tsc_value = _rdtsc();
@ -61,7 +51,54 @@ fn tsc_mode_init() {
APIC_TIMER_CALLBACK.call_once(|| Arc::new(callback)); APIC_TIMER_CALLBACK.call_once(|| Arc::new(callback));
} }
fn periodic_mode_init() { /// When kernel cannot get the TSC frequency from CPUID, it can leverage
/// the PIT to calculate this frequency.
fn determine_tsc_freq_via_pit() -> u64 {
let mut irq = IrqLine::alloc_specific(super::TIMER_IRQ_NUM.load(Ordering::Relaxed)).unwrap();
irq.on_active(pit_callback);
let mut io_apic = IO_APIC.get().unwrap().get(0).unwrap().lock();
debug_assert_eq!(io_apic.interrupt_base(), 0);
io_apic.enable(2, irq.clone()).unwrap();
drop(io_apic);
super::pit::init();
x86_64::instructions::interrupts::enable();
static IS_FINISH: AtomicBool = AtomicBool::new(false);
static FREQUENCY: AtomicU64 = AtomicU64::new(0);
while !IS_FINISH.load(Ordering::Acquire) {
x86_64::instructions::hlt();
}
x86_64::instructions::interrupts::disable();
drop(irq);
return FREQUENCY.load(Ordering::Acquire);
fn pit_callback(trap_frame: &TrapFrame) {
static mut IN_TIME: u64 = 0;
static mut TSC_FIRST_COUNT: u64 = 0;
// Set a certain times of callbacks to calculate the frequency.
const CALLBACK_TIMES: u64 = TIMER_FREQ / 10;
unsafe {
if IN_TIME < CALLBACK_TIMES || IS_FINISH.load(Ordering::Acquire) {
// drop the first entry, since it may not be the time we want
if IN_TIME == 0 {
TSC_FIRST_COUNT = _rdtsc();
}
IN_TIME += 1;
return;
}
let mut io_apic = IO_APIC.get().unwrap().get(0).unwrap().lock();
io_apic.disable(2).unwrap();
drop(io_apic);
let tsc_count = _rdtsc();
let freq = (tsc_count - TSC_FIRST_COUNT) * (TIMER_FREQ / CALLBACK_TIMES);
FREQUENCY.store(freq, Ordering::Release);
}
IS_FINISH.store(true, Ordering::Release);
}
}
fn init_periodic_mode() {
let mut apic_lock = APIC_INSTANCE.get().unwrap().lock(); let mut apic_lock = APIC_INSTANCE.get().unwrap().lock();
let mut irq = IrqLine::alloc_specific(super::TIMER_IRQ_NUM.load(Ordering::Relaxed)).unwrap(); let mut irq = IrqLine::alloc_specific(super::TIMER_IRQ_NUM.load(Ordering::Relaxed)).unwrap();
irq.on_active(init_function); irq.on_active(init_function);
@ -108,7 +145,6 @@ fn periodic_mode_init() {
apic_lock.set_timer_init_count(ticks); apic_lock.set_timer_init_count(ticks);
apic_lock.set_lvt_timer(super::TIMER_IRQ_NUM.load(Ordering::Relaxed) as u64 | (1 << 17)); apic_lock.set_lvt_timer(super::TIMER_IRQ_NUM.load(Ordering::Relaxed) as u64 | (1 << 17));
apic_lock.set_timer_div_config(DivideConfig::Divide64); apic_lock.set_timer_div_config(DivideConfig::Divide64);
info!( info!(
"APIC Timer ticks count:{:x}, remain ticks: {:x},Timer Freq:{} Hz", "APIC Timer ticks count:{:x}, remain ticks: {:x},Timer Freq:{} Hz",
ticks, remain_ticks, TIMER_FREQ ticks, remain_ticks, TIMER_FREQ

View File

@ -7,6 +7,7 @@ edition = "2021"
[dependencies] [dependencies]
jinux-frame = { path = "../../../framework/jinux-frame" } jinux-frame = { path = "../../../framework/jinux-frame" }
jinux-util = { path = "../../libs/jinux-util" }
component = { path = "../../libs/comp-sys/component" } component = { path = "../../libs/comp-sys/component" }
log = "0.4" log = "0.4"
spin = "0.9.4" spin = "0.9.4"

View File

@ -0,0 +1,218 @@
//! This module provides abstractions for hardware-assisted timing mechanisms, encapsulated by the `ClockSource` struct.
//! A `ClockSource` can be constructed from any counter with a stable frequency, enabling precise 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 core::{cmp::max, ops::Add, time::Duration};
use jinux_frame::sync::SpinLock;
use jinux_util::coeff::Coeff;
use crate::NANOS_PER_SECOND;
/// `ClockSource` is an abstraction for hardware-assisted timing mechanisms.
/// A `ClockSource` can be created based on any counter that operates at a stable frequency.
/// Users are able to measure time by retrieving `Instant` from this source.
///
/// # Implementation
/// 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.
/// 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**.
/// The maximum interval for these updates must be determined at the time of the `ClockSource` initialization.
///
/// # Examples
/// Suppose we have a counter called `counter` which have the frequency `counter.freq`, and the method to read its cycles called `read_counter()`.
/// We can create a corresponding `ClockSource` and use it as follows:
///
/// ```rust
/// // here we set the max_delay_secs = 10
/// let max_delay_secs = 10;
/// // create a clocksource named counter_clock
/// let counter_clock = ClockSource::new(counter.freq, max_delay_secs, Arc::new(read_counter));
/// // read an instant.
/// let instant = counter_clock.read_instant();
/// ```
///
/// If using this `ClockSource`, you must ensure its internal instant will be updated
/// at least once within a time interval of not more than `max_delay_secs.
pub struct ClockSource {
read_cycles: Arc<dyn Fn() -> u64 + Sync + Send>,
base: ClockSourceBase,
coeff: Coeff,
last_instant: SpinLock<Instant>,
last_cycles: SpinLock<u64>,
}
impl ClockSource {
/// Create a new `ClockSource` instance.
/// Require basic information of based time counter, including the function for reading cycles, 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.
/// This `Coeff` is used to convert the number of cycles into the duration of time that has passed for those cycles.
pub fn new(
freq: u64,
max_delay_secs: u64,
read_cycles: Arc<dyn Fn() -> u64 + Sync + Send>,
) -> Self {
let base = ClockSourceBase::new(freq, max_delay_secs);
// Too big `max_delay_secs` will lead to a low resolution Coeff.
debug_assert!(max_delay_secs < 600);
let coeff = Coeff::new(NANOS_PER_SECOND as u64, freq, max_delay_secs * freq);
Self {
read_cycles,
base,
coeff,
last_instant: SpinLock::new(Instant::zero()),
last_cycles: SpinLock::new(0),
}
}
fn cycles_to_nanos(&self, cycles: u64) -> u64 {
self.coeff * cycles
}
/// 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.
fn calculate_instant(&self, instant_cycles: u64) -> Instant {
let delta_nanos = {
let delta_cycles = instant_cycles - self.last_cycles();
self.cycles_to_nanos(delta_cycles)
};
let duration = Duration::from_nanos(delta_nanos);
self.last_instant() + duration
}
/// Use an input instant to update the internal instant in the `ClockSource`.
fn update_last_instant(&self, instant: &Instant) {
*self.last_instant.lock() = *instant;
}
/// 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`.
pub fn read_cycles(&self) -> u64 {
(self.read_cycles)()
}
/// Return the last instant recorded in the `ClockSource`.
pub fn last_instant(&self) -> Instant {
return *self.last_instant.lock();
}
/// 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`.
pub fn max_delay_secs(&self) -> u64 {
self.base.max_delay_secs()
}
/// Return the reference to the generated cycles coeff of the `ClockSource`.
pub fn coeff(&self) -> &Coeff {
&self.coeff
}
/// Return the frequency of the counter used in the `ClockSource`.
pub fn freq(&self) -> u64 {
self.base.freq()
}
/// Calibrate the recorded `Instant` to zero, and record the instant cycles.
pub(crate) fn calibrate(&self, instant_cycles: u64) {
self.update_last_cycles(instant_cycles);
self.update_last_instant(&Instant::zero());
}
/// Get the instant to update the internal instant in the `ClockSource`.
pub(crate) fn update(&self) {
let instant_cycles = self.read_cycles();
let instant = self.calculate_instant(instant_cycles);
self.update_last_cycles(instant_cycles);
self.update_last_instant(&instant);
}
/// 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.
/// Then leverage it to calculate the instant.
pub(crate) fn read_instant(&self) -> Instant {
let instant_cycles = self.read_cycles();
self.calculate_instant(instant_cycles)
}
}
/// `Instant` captures a specific moment, storing the duration of time
/// elapsed since a reference point (typically the system boot time).
/// The `Instant` is expressed in seconds and the fractional part is expressed in nanoseconds.
#[derive(Debug, Default, Copy, Clone)]
pub struct Instant {
secs: u64,
nanos: u32,
}
impl Instant {
pub const fn zero() -> Self {
Self { secs: 0, nanos: 0 }
}
pub fn new(secs: u64, nanos: u32) -> Self {
Self { secs, nanos }
}
/// Return the seconds recorded in the Instant.
pub fn secs(&self) -> u64 {
self.secs
}
/// Return the nanoseconds recorded in the Instant.
pub fn nanos(&self) -> u32 {
self.nanos
}
}
impl Add<Duration> for Instant {
type Output = Instant;
fn add(self, other: Duration) -> Self::Output {
let mut secs = self.secs + other.as_secs();
let mut nanos = self.nanos + other.subsec_nanos();
if nanos >= NANOS_PER_SECOND {
secs += 1;
nanos -= NANOS_PER_SECOND;
}
Instant::new(secs, nanos)
}
}
/// The basic properties of `ClockSource`.
#[derive(Debug, Copy, Clone)]
struct ClockSourceBase {
freq: u64,
max_delay_secs: u64,
}
impl ClockSourceBase {
fn new(freq: u64, max_delay_secs: u64) -> Self {
let max_delay_secs = max(2, max_delay_secs);
ClockSourceBase {
freq,
max_delay_secs,
}
}
fn max_delay_secs(&self) -> u64 {
self.max_delay_secs
}
fn freq(&self) -> u64 {
self.freq
}
}

View File

@ -2,15 +2,30 @@
#![no_std] #![no_std]
#![forbid(unsafe_code)] #![forbid(unsafe_code)]
use component::{init_component, ComponentInitError}; extern crate alloc;
use core::sync::atomic::Ordering::Relaxed;
use rtc::{get_cmos, is_updating, read, CENTURY_REGISTER};
use alloc::sync::Arc;
use component::{init_component, ComponentInitError};
use core::{sync::atomic::Ordering::Relaxed, time::Duration};
use jinux_frame::sync::Mutex;
use spin::Once;
use clocksource::ClockSource;
use rtc::{get_cmos, is_updating, CENTURY_REGISTER};
pub use clocksource::Instant;
mod clocksource;
mod rtc; mod rtc;
mod tsc;
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();
#[init_component] #[init_component]
fn time_init() -> Result<(), ComponentInitError> { fn time_init() -> Result<(), ComponentInitError> {
rtc::init(); rtc::init();
tsc::init();
Ok(()) Ok(())
} }
@ -23,6 +38,7 @@ pub struct SystemTime {
pub hour: u8, pub hour: u8,
pub minute: u8, pub minute: u8,
pub second: u8, pub second: u8,
pub nanos: u64,
} }
impl SystemTime { impl SystemTime {
@ -35,6 +51,7 @@ impl SystemTime {
hour: 0, hour: 0,
minute: 0, minute: 0,
second: 0, second: 0,
nanos: 0,
} }
} }
@ -88,7 +105,55 @@ impl SystemTime {
} }
} }
pub(crate) static READ_TIME: Mutex<SystemTime> = Mutex::new(SystemTime::zero());
pub(crate) static START_TIME: Once<SystemTime> = Once::new();
/// get real time /// get real time
pub fn get_real_time() -> SystemTime { pub fn get_real_time() -> SystemTime {
read() read()
} }
pub fn read() -> SystemTime {
update_time();
*READ_TIME.lock()
}
/// read year,month,day and other data
/// ref: https://wiki.osdev.org/CMOS#Reading_All_RTC_Time_and_Date_Registers
fn update_time() {
let mut last_time: SystemTime;
let mut lock = READ_TIME.lock();
lock.update_from_rtc();
last_time = *lock;
lock.update_from_rtc();
while *lock != last_time {
last_time = *lock;
lock.update_from_rtc();
}
let register_b: u8 = get_cmos(0x0B);
lock.convert_bcd_to_binary(register_b);
lock.convert_12_hour_to_24_hour(register_b);
lock.modify_year();
}
/// Return the `START_TIME`, which is the actual time when doing calibrate.
pub fn read_start_time() -> SystemTime {
*START_TIME.get().unwrap()
}
/// Return the monotonic time from the tsc clocksource.
pub fn read_monotonic_time() -> Duration {
let instant = tsc::read_instant();
Duration::new(instant.secs(), instant.nanos())
}
/// Return the tsc clocksource.
pub fn default_clocksource() -> Arc<ClockSource> {
tsc::CLOCK.get().unwrap().clone()
}

View File

@ -1,15 +1,9 @@
use core::sync::atomic::AtomicU8; use core::sync::atomic::AtomicU8;
use core::sync::atomic::Ordering::Relaxed; use core::sync::atomic::Ordering::Relaxed;
use crate::SystemTime;
use jinux_frame::arch::x86::device::cmos::{get_century_register, CMOS_ADDRESS, CMOS_DATA}; use jinux_frame::arch::x86::device::cmos::{get_century_register, CMOS_ADDRESS, CMOS_DATA};
use jinux_frame::sync::Mutex;
pub(crate) static CENTURY_REGISTER: AtomicU8 = AtomicU8::new(0); pub(crate) static CENTURY_REGISTER: AtomicU8 = AtomicU8::new(0);
static READ_TIME: Mutex<SystemTime> = Mutex::new(SystemTime::zero());
pub fn init() { pub fn init() {
let Some(century_register) = get_century_register() else { let Some(century_register) = get_century_register() else {
return; return;
@ -26,34 +20,3 @@ pub fn is_updating() -> bool {
CMOS_ADDRESS.write(0x0A); CMOS_ADDRESS.write(0x0A);
CMOS_DATA.read() & 0x80 != 0 CMOS_DATA.read() & 0x80 != 0
} }
pub fn read() -> SystemTime {
update_time();
*READ_TIME.lock()
}
/// read year,month,day and other data
/// ref: https://wiki.osdev.org/CMOS#Reading_All_RTC_Time_and_Date_Registers
fn update_time() {
let mut last_time: SystemTime;
let mut lock = READ_TIME.lock();
lock.update_from_rtc();
last_time = *lock;
lock.update_from_rtc();
while *lock != last_time {
last_time = *lock;
lock.update_from_rtc();
}
let register_b: u8 = get_cmos(0x0B);
lock.convert_bcd_to_binary(register_b);
lock.convert_12_hour_to_24_hour(register_b);
lock.modify_year();
}

View File

@ -0,0 +1,72 @@
//! This module provide a instance of `ClockSource` based on TSC.
//!
//! Use `init` to initialize this module.
use alloc::sync::Arc;
use core::time::Duration;
use jinux_frame::arch::{read_tsc, x86::tsc_freq};
use jinux_frame::timer::Timer;
use spin::Once;
use crate::clocksource::{ClockSource, Instant};
use crate::{START_TIME, VDSO_DATA_UPDATE};
/// A instance of TSC clocksource.
pub static CLOCK: Once<Arc<ClockSource>> = Once::new();
const MAX_DELAY_SECS: u64 = 60;
/// Init tsc clocksource module.
pub(super) fn init() {
init_clock();
calibrate();
init_timer();
}
fn init_clock() {
CLOCK.call_once(|| {
Arc::new(ClockSource::new(
tsc_freq(),
MAX_DELAY_SECS,
Arc::new(read_tsc),
))
});
}
/// Calibrate the TSC and system time based on the RTC time.
fn calibrate() {
let clock = CLOCK.get().unwrap();
let cycles = clock.read_cycles();
clock.calibrate(cycles);
START_TIME.call_once(crate::read);
}
/// Read an `Instant` of tsc clocksource.
pub(super) fn read_instant() -> Instant {
let clock = CLOCK.get().unwrap();
clock.read_instant()
}
fn update_clocksource(timer: Arc<Timer>) {
let clock = CLOCK.get().unwrap();
clock.update();
// Update vdso data.
if VDSO_DATA_UPDATE.is_completed() {
VDSO_DATA_UPDATE.get().unwrap()(clock.last_instant(), clock.last_cycles());
}
// 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.
timer.set(Duration::from_secs(clock.max_delay_secs() - 1));
}
fn init_timer() {
let timer = Timer::new(update_clocksource).unwrap();
// The initial timer should be set as `clock.max_delay_secs() >> 1` or something much smaller than `max_delay_secs`.
// This is because the initialization of this timer occurs during system startup.
// Afterwards, the system will undergo additional initialization processes, during which time interrupts are disabled.
// This results in the actual trigger time of the timer being delayed by about 5 seconds compared to the set time.
// TODO: This is a temporary handle, and should be modified in the future.
timer.set(Duration::from_secs(
CLOCK.get().unwrap().max_delay_secs() >> 1,
));
}

View File

@ -3,8 +3,10 @@ use core::time::Duration;
use crate::prelude::*; use crate::prelude::*;
use jinux_time::read_monotonic_time;
mod system_time; mod system_time;
use jinux_frame::timer::read_monotonic_milli_seconds;
pub use system_time::SystemTime; pub use system_time::SystemTime;
pub type clockid_t = i32; pub type clockid_t = i32;
@ -76,13 +78,8 @@ pub fn now_as_duration(clock_id: &ClockID) -> Result<Duration> {
match clock_id { match clock_id {
ClockID::CLOCK_MONOTONIC ClockID::CLOCK_MONOTONIC
| ClockID::CLOCK_MONOTONIC_COARSE | ClockID::CLOCK_MONOTONIC_COARSE
| ClockID::CLOCK_MONOTONIC_RAW => { | ClockID::CLOCK_MONOTONIC_RAW
let time_ms = read_monotonic_milli_seconds(); | ClockID::CLOCK_BOOTTIME => Ok(read_monotonic_time()),
let seconds = time_ms / 1000;
let nanos = time_ms % 1000 * 1_000_000;
Ok(Duration::new(seconds, nanos as u32))
}
ClockID::CLOCK_REALTIME | ClockID::CLOCK_REALTIME_COARSE => { ClockID::CLOCK_REALTIME | ClockID::CLOCK_REALTIME_COARSE => {
let now = SystemTime::now(); let now = SystemTime::now();
now.duration_since(&SystemTime::UNIX_EPOCH) now.duration_since(&SystemTime::UNIX_EPOCH)

View File

@ -1,7 +1,8 @@
use core::time::Duration; use core::time::Duration;
use jinux_time::{read_monotonic_time, read_start_time};
use time::{Date, Month, PrimitiveDateTime, Time};
use crate::prelude::*; use crate::prelude::*;
use time::{Date, Month, PrimitiveDateTime, Time};
/// This struct corresponds to `SystemTime` in Rust std. /// This struct corresponds to `SystemTime` in Rust std.
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] #[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
@ -25,9 +26,13 @@ impl SystemTime {
/// Returns the current system time /// Returns the current system time
pub fn now() -> Self { pub fn now() -> Self {
let system_time = jinux_time::get_real_time(); let start = read_start_time();
// The get real time result should always be valid // The get real time result should always be valid
convert_system_time(system_time).unwrap() convert_system_time(start)
.unwrap()
.checked_add(read_monotonic_time())
.unwrap()
} }
/// Add a duration to self. If the result does not exceed inner bounds return Some(t), else return None. /// Add a duration to self. If the result does not exceed inner bounds return Some(t), else return None.
@ -73,7 +78,12 @@ fn convert_system_time(system_time: jinux_time::SystemTime) -> Result<SystemTime
Ok(date) => date, Ok(date) => date,
Err(_) => return_errno_with_message!(Errno::EINVAL, "Invalid system date"), Err(_) => return_errno_with_message!(Errno::EINVAL, "Invalid system date"),
}; };
let time_ = match Time::from_hms(system_time.hour, system_time.minute, system_time.second) { let time_ = match Time::from_hms_nano(
system_time.hour,
system_time.minute,
system_time.second,
system_time.nanos.try_into().unwrap(),
) {
Ok(time_) => time_, Ok(time_) => time_,
Err(_) => return_errno_with_message!(Errno::EINVAL, "Invalid system time"), Err(_) => return_errno_with_message!(Errno::EINVAL, "Invalid system time"),
}; };

View File

@ -12,5 +12,5 @@ typeflags-util = { path = "../typeflags-util" }
jinux-rights-proc = { path = "../jinux-rights-proc" } jinux-rights-proc = { path = "../jinux-rights-proc" }
jinux-rights = { path = "../jinux-rights" } jinux-rights = { path = "../jinux-rights" }
bitvec = { version = "1.0", default-features = false, features = ["alloc"] } bitvec = { version = "1.0", default-features = false, features = ["alloc"] }
ktest = { path = "../../../framework/libs/ktest" }
[features] [features]

View File

@ -0,0 +1,139 @@
//! This module provides an abstraction `Coeff` to server for efficient and accurate calculation
//! of fraction multiplication.
use core::ops::Mul;
use ktest::if_cfg_ktest;
/// A `Coeff` is used to do a fraction multiplication operation with an unsigned integer.
/// It can achieve accurate and efficient calculation and avoid numeric overflow at the same time.
///
/// # Example
///
/// Let's say we want to multiply a fraction (23456 / 56789) with the target integer `a`,
/// which will be no larger than `1_000_000_000`, we can use the following code snippets
/// to get an accurate result.
///
/// ```
/// let a = input();
/// let coeff = Coeff::new(23456, 56789, 1_000_000_000);
/// let result = coeff * a;
/// ```
///
/// # How it works
/// `Coeff` is used in the calculation of a fraction value multiplied by an integer.
/// Here is a simple example of such calculation:
///
/// ```rust
/// let result = (a / b) * c;
/// ```
///
/// In this equation, `a`, `b`, `c` and `result` are all integers. To acquire a more precise result, we will
/// generally calculate `a * c` first and then divide the multiplication result with `b`.
/// However, this simple calculation above has two complications:
/// - The calculation of `a * c` may overflow if they are too large.
/// - The division operation is much more expensive than integer multiplication, which can easily create performance bottlenecks.
///
/// `Coeff` is implemented to address these two issues. It can be used to replace the fraction in this calculation.
/// For example, a `Coeff` generated from (a / b) can modify the calculation above to ensure that:
///
/// ```
/// coeff * c ~= (a / b) * c
/// ```
///
/// In principle, `Coeff` actually turns the multiplication and division into a combination of multiplication and bit operation.
/// When creating a `Coeff`, it needs to know the numerator and denominator of the represented fraction
/// and the max multiplier it will be multiplied by. Then, a `mult` and a `shift` will be chosen to achieve the replacement of calculation.
/// Taking the previous calculation as an example again, `coeff * c` will turn into `mult * c >> shift`, ensuring that:
///
/// ```
/// mult * c >> shift ~= (a / b) * c
/// ```
///
/// and
///
/// `mult * c` will not result in numeric overflow (i.e., `mult * c` will stay below MAX_U64).
///
/// This is how `Coeff` achieves accuracy and efficiency at the same time.
#[derive(Debug, Copy, Clone)]
pub struct Coeff {
mult: u32,
shift: u32,
max_multiplier: u64,
}
impl Coeff {
/// Create a new coeff, which is essentially equivalent to `numerator` / `denominator`) when being multiplied to an integer;
/// Here users should make sure the multiplied integer should not be larger than `max_multiplier`.
pub fn new(numerator: u64, denominator: u64, max_multiplier: u64) -> Self {
let mut shift_acc: u32 = 32;
// Too large `max_multiplier` will make the generated coeff imprecise
debug_assert!(max_multiplier < (1 << 40));
let mut tmp = max_multiplier >> 32;
// Counts the number of 0 in front of the `max_multiplier`.
// `shift_acc` indicates the maximum number of bits `mult` can have.
while tmp > 0 {
tmp >>= 1;
shift_acc -= 1;
}
// Try the `shift` from 32 to 0.
let mut shift = 32;
let mut mult = 0;
while shift > 0 {
mult = numerator << shift;
mult += denominator / 2;
mult /= denominator;
if (mult >> shift_acc) == 0 {
break;
}
shift -= 1;
}
Self {
mult: mult as u32,
shift,
max_multiplier,
}
}
/// Return the `mult` of the Coeff.
/// Only used for the VdsoData and will be removed in the future.
pub fn mult(&self) -> u32 {
self.mult
}
/// Return the `shift` of the Coeff.
/// Only used for the VdsoData and will be removed in the future.
pub fn shift(&self) -> u32 {
self.shift
}
}
impl Mul<u64> for Coeff {
type Output = u64;
fn mul(self, rhs: u64) -> Self::Output {
debug_assert!(rhs <= self.max_multiplier);
(rhs * self.mult as u64) >> self.shift
}
}
impl Mul<u32> for Coeff {
type Output = u32;
fn mul(self, rhs: u32) -> Self::Output {
debug_assert!(rhs as u64 <= self.max_multiplier);
((rhs as u64 * self.mult as u64) >> self.shift) as u32
}
}
#[if_cfg_ktest]
mod test {
use super::*;
use ktest::ktest;
#[ktest]
fn calculation() {
let coeff = Coeff::new(23456, 56789, 1_000_000_000);
assert!(coeff * 0 as u64 == 0);
assert!(coeff * 100 as u64 == 100 * 23456 / 56789);
assert!(coeff * 1_000_000_000 as u64 == 1_000_000_000 * 23456 / 56789);
}
}

View File

@ -4,6 +4,7 @@
extern crate alloc; extern crate alloc;
pub mod coeff;
pub mod dup; pub mod dup;
pub mod id_allocator; pub mod id_allocator;
pub mod safe_ptr; pub mod safe_ptr;