mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-28 03:43:23 +00:00
Implement a high precision gettime based on tsc
This commit is contained in:
committed by
Tate, Hongliang Tian
parent
ba08895fc3
commit
715072b9f3
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -804,6 +804,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"component",
|
||||
"jinux-frame",
|
||||
"jinux-util",
|
||||
"log",
|
||||
"spin 0.9.8",
|
||||
]
|
||||
@ -816,6 +817,7 @@ dependencies = [
|
||||
"jinux-frame",
|
||||
"jinux-rights",
|
||||
"jinux-rights-proc",
|
||||
"ktest",
|
||||
"pod",
|
||||
"typeflags-util",
|
||||
]
|
||||
|
@ -1,11 +1,23 @@
|
||||
use core::sync::atomic::AtomicU64;
|
||||
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
|
||||
/// 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`
|
||||
///
|
||||
pub fn tsc_freq() -> Option<u32> {
|
||||
pub fn determine_tsc_freq_via_cpuid() -> Option<u32> {
|
||||
// Check the max cpuid supported
|
||||
let cpuid = cpuid!(0);
|
||||
let max_cpuid = cpuid.eax;
|
||||
|
@ -12,6 +12,8 @@ pub mod qemu;
|
||||
pub(crate) mod tdx_guest;
|
||||
pub(crate) mod timer;
|
||||
|
||||
use core::{arch::x86_64::_rdtsc, sync::atomic::Ordering};
|
||||
|
||||
use kernel::apic::ioapic;
|
||||
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 processor’s 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() {
|
||||
use x86_64::registers::{control::Cr4Flags, model_specific::EferFlags, xcontrol::XCr0Flags};
|
||||
let mut cr4 = x86_64::registers::control::Cr4::read();
|
||||
|
@ -1,55 +1,45 @@
|
||||
use core::arch::x86_64::_rdtsc;
|
||||
use core::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
use alloc::sync::Arc;
|
||||
use core::arch::x86_64::_rdtsc;
|
||||
use core::sync::atomic::{AtomicBool, AtomicU64, Ordering};
|
||||
|
||||
use log::info;
|
||||
use spin::Once;
|
||||
use trapframe::TrapFrame;
|
||||
use x86::cpuid::cpuid;
|
||||
use x86::msr::{wrmsr, IA32_TSC_DEADLINE};
|
||||
|
||||
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::{
|
||||
arch::x86::kernel::{
|
||||
apic::{DivideConfig, APIC_INSTANCE},
|
||||
tsc::tsc_freq,
|
||||
},
|
||||
config::TIMER_FREQ,
|
||||
};
|
||||
|
||||
pub fn init() {
|
||||
if tsc_mode_support() {
|
||||
info!("[Timer]: Enable APIC-TSC deadline mode.");
|
||||
tsc_mode_init();
|
||||
init_tsc_freq();
|
||||
if is_tsc_deadline_mode_supported() {
|
||||
info!("[Timer]: Enable APIC TSC deadline mode.");
|
||||
init_tsc_mode();
|
||||
} else {
|
||||
info!("[Timer]: Enable APIC-periodic mode.");
|
||||
periodic_mode_init();
|
||||
info!("[Timer]: Enable APIC periodic mode.");
|
||||
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();
|
||||
|
||||
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();
|
||||
// Enable tsc deadline mode.
|
||||
apic_lock.set_lvt_timer(super::TIMER_IRQ_NUM.load(Ordering::Relaxed) as u64 | (1 << 18));
|
||||
|
||||
let tsc_step = {
|
||||
let tsc_rate = tsc_freq().unwrap() as u64;
|
||||
info!("TSC frequency:{:?} Hz", tsc_rate * 1000);
|
||||
tsc_rate * 1000 / TIMER_FREQ
|
||||
};
|
||||
drop(apic_lock);
|
||||
let tsc_step = TSC_FREQ.load(Ordering::Relaxed) / TIMER_FREQ;
|
||||
|
||||
let callback = move || unsafe {
|
||||
let tsc_value = _rdtsc();
|
||||
@ -61,7 +51,54 @@ fn tsc_mode_init() {
|
||||
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 irq = IrqLine::alloc_specific(super::TIMER_IRQ_NUM.load(Ordering::Relaxed)).unwrap();
|
||||
irq.on_active(init_function);
|
||||
@ -108,7 +145,6 @@ fn periodic_mode_init() {
|
||||
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_timer_div_config(DivideConfig::Divide64);
|
||||
|
||||
info!(
|
||||
"APIC Timer ticks count:{:x}, remain ticks: {:x},Timer Freq:{} Hz",
|
||||
ticks, remain_ticks, TIMER_FREQ
|
||||
|
@ -7,6 +7,7 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
jinux-frame = { path = "../../../framework/jinux-frame" }
|
||||
jinux-util = { path = "../../libs/jinux-util" }
|
||||
component = { path = "../../libs/comp-sys/component" }
|
||||
log = "0.4"
|
||||
spin = "0.9.4"
|
||||
|
218
services/comps/time/src/clocksource.rs
Normal file
218
services/comps/time/src/clocksource.rs
Normal 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
|
||||
}
|
||||
}
|
@ -2,15 +2,30 @@
|
||||
#![no_std]
|
||||
#![forbid(unsafe_code)]
|
||||
|
||||
use component::{init_component, ComponentInitError};
|
||||
use core::sync::atomic::Ordering::Relaxed;
|
||||
use rtc::{get_cmos, is_updating, read, CENTURY_REGISTER};
|
||||
extern crate alloc;
|
||||
|
||||
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 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]
|
||||
fn time_init() -> Result<(), ComponentInitError> {
|
||||
rtc::init();
|
||||
tsc::init();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -23,6 +38,7 @@ pub struct SystemTime {
|
||||
pub hour: u8,
|
||||
pub minute: u8,
|
||||
pub second: u8,
|
||||
pub nanos: u64,
|
||||
}
|
||||
|
||||
impl SystemTime {
|
||||
@ -35,6 +51,7 @@ impl SystemTime {
|
||||
hour: 0,
|
||||
minute: 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
|
||||
pub fn get_real_time() -> SystemTime {
|
||||
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()
|
||||
}
|
||||
|
@ -1,15 +1,9 @@
|
||||
use core::sync::atomic::AtomicU8;
|
||||
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::sync::Mutex;
|
||||
|
||||
pub(crate) static CENTURY_REGISTER: AtomicU8 = AtomicU8::new(0);
|
||||
|
||||
static READ_TIME: Mutex<SystemTime> = Mutex::new(SystemTime::zero());
|
||||
|
||||
pub fn init() {
|
||||
let Some(century_register) = get_century_register() else {
|
||||
return;
|
||||
@ -26,34 +20,3 @@ pub fn is_updating() -> bool {
|
||||
CMOS_ADDRESS.write(0x0A);
|
||||
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();
|
||||
}
|
||||
|
72
services/comps/time/src/tsc.rs
Normal file
72
services/comps/time/src/tsc.rs
Normal 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,
|
||||
));
|
||||
}
|
@ -3,8 +3,10 @@ use core::time::Duration;
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
use jinux_time::read_monotonic_time;
|
||||
|
||||
mod system_time;
|
||||
use jinux_frame::timer::read_monotonic_milli_seconds;
|
||||
|
||||
pub use system_time::SystemTime;
|
||||
|
||||
pub type clockid_t = i32;
|
||||
@ -76,13 +78,8 @@ pub fn now_as_duration(clock_id: &ClockID) -> Result<Duration> {
|
||||
match clock_id {
|
||||
ClockID::CLOCK_MONOTONIC
|
||||
| ClockID::CLOCK_MONOTONIC_COARSE
|
||||
| ClockID::CLOCK_MONOTONIC_RAW => {
|
||||
let time_ms = read_monotonic_milli_seconds();
|
||||
|
||||
let seconds = time_ms / 1000;
|
||||
let nanos = time_ms % 1000 * 1_000_000;
|
||||
Ok(Duration::new(seconds, nanos as u32))
|
||||
}
|
||||
| ClockID::CLOCK_MONOTONIC_RAW
|
||||
| ClockID::CLOCK_BOOTTIME => Ok(read_monotonic_time()),
|
||||
ClockID::CLOCK_REALTIME | ClockID::CLOCK_REALTIME_COARSE => {
|
||||
let now = SystemTime::now();
|
||||
now.duration_since(&SystemTime::UNIX_EPOCH)
|
||||
|
@ -1,7 +1,8 @@
|
||||
use core::time::Duration;
|
||||
use jinux_time::{read_monotonic_time, read_start_time};
|
||||
use time::{Date, Month, PrimitiveDateTime, Time};
|
||||
|
||||
use crate::prelude::*;
|
||||
use time::{Date, Month, PrimitiveDateTime, Time};
|
||||
|
||||
/// This struct corresponds to `SystemTime` in Rust std.
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
@ -25,9 +26,13 @@ impl SystemTime {
|
||||
|
||||
/// Returns the current system time
|
||||
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
|
||||
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.
|
||||
@ -73,7 +78,12 @@ fn convert_system_time(system_time: jinux_time::SystemTime) -> Result<SystemTime
|
||||
Ok(date) => 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_,
|
||||
Err(_) => return_errno_with_message!(Errno::EINVAL, "Invalid system time"),
|
||||
};
|
||||
|
@ -12,5 +12,5 @@ typeflags-util = { path = "../typeflags-util" }
|
||||
jinux-rights-proc = { path = "../jinux-rights-proc" }
|
||||
jinux-rights = { path = "../jinux-rights" }
|
||||
bitvec = { version = "1.0", default-features = false, features = ["alloc"] }
|
||||
|
||||
ktest = { path = "../../../framework/libs/ktest" }
|
||||
[features]
|
||||
|
139
services/libs/jinux-util/src/coeff.rs
Normal file
139
services/libs/jinux-util/src/coeff.rs
Normal 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);
|
||||
}
|
||||
}
|
@ -4,6 +4,7 @@
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
pub mod coeff;
|
||||
pub mod dup;
|
||||
pub mod id_allocator;
|
||||
pub mod safe_ptr;
|
||||
|
Reference in New Issue
Block a user