devices: pit: remove clock_gettime() usage

The original Pit code encoded the start value of a timer into the
count_load_time field as a number of nanoseconds since the start of
the host's monotonic time value. Instead, we can use the PitCounter's
creation_time as the epoch. This makes the math look a bit more sensible
and removes the use of the non-portable clock_gettime() function.

Before
======

get_channel_state():
    count_load_time = get_monotonic_time() - start.elapsed()
=== count_load_time = now() - (now() - start)
=== count_load_time = start

set_channel_state():
    start = now() - (get_monotonic_time() - count_load_time)
=== start = now() - now() + count_load_time
=== start = count_load_time

After
=====

get_channel_state():
    count_load_time = start - creation_time

set_channel_state():
    start = creation_time + count_load_time

BUG=chromium:908689
BUG=b:213149155
TEST=cargo test -p devices pit
TEST=tools/run_tests --target=host --arch=win64

Change-Id: I5468d1d964a04b1ce96979ed583b729d139e1005
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/3723804
Reviewed-by: Noah Gold <nkgold@google.com>
Reviewed-by: Vikram Auradkar <auradkar@google.com>
Tested-by: kokoro <noreply+kokoro@google.com>
Commit-Queue: Daniel Verkamp <dverkamp@chromium.org>
This commit is contained in:
Daniel Verkamp 2022-06-24 17:14:25 -07:00 committed by Chromeos LUCI
parent aeea40d6ff
commit e5c6e65731
3 changed files with 44 additions and 34 deletions

View file

@ -24,10 +24,22 @@ impl Clock {
self.0.duration_since(earlier.0)
}
pub fn checked_duration_since(&self, earlier: &Self) -> Option<Duration> {
self.0.checked_duration_since(earlier.0)
}
pub fn saturating_duration_since(&self, earlier: &Self) -> Duration {
self.0.saturating_duration_since(earlier.0)
}
pub fn elapsed(&self) -> Duration {
self.0.elapsed()
}
pub fn checked_add(&self, duration: Duration) -> Option<Self> {
Some(Clock(self.0.checked_add(duration)?))
}
pub fn checked_sub(&self, duration: Duration) -> Option<Self> {
Some(Clock(self.0.checked_sub(duration)?))
}
@ -71,8 +83,22 @@ impl FakeClock {
/// Get the duration since |earlier|, assuming that earlier < self.
pub fn duration_since(&self, earlier: &Self) -> Duration {
let ns_diff = self.ns_since_epoch - earlier.ns_since_epoch;
Duration::new(ns_diff / NS_PER_SEC, (ns_diff % NS_PER_SEC) as u32)
self.checked_duration_since(earlier).unwrap()
}
/// Get the duration since |earlier|
pub fn checked_duration_since(&self, earlier: &Self) -> Option<Duration> {
let ns_diff = self.ns_since_epoch.checked_sub(earlier.ns_since_epoch)?;
Some(Duration::new(
ns_diff / NS_PER_SEC,
(ns_diff % NS_PER_SEC) as u32,
))
}
/// Get the duration since |earlier|, or 0 if earlier < self.
pub fn saturating_duration_since(&self, earlier: &Self) -> Duration {
self.checked_duration_since(earlier)
.unwrap_or(Duration::ZERO)
}
/// Get the time that has elapsed since this clock was made. Always returns 0 on a FakeClock.
@ -80,6 +106,15 @@ impl FakeClock {
self.now().duration_since(self)
}
pub fn checked_add(&self, duration: Duration) -> Option<Self> {
Some(FakeClock {
ns_since_epoch: self
.ns_since_epoch
.checked_add(duration.as_nanos() as u64)?,
deadlines: Vec::new(),
})
}
pub fn checked_sub(&self, duration: Duration) -> Option<Self> {
Some(FakeClock {
ns_since_epoch: self

View file

@ -10,6 +10,8 @@ mod bus;
mod irq_event;
pub mod irqchip;
mod pci;
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
mod pit;
pub mod serial_device;
mod sys;
pub mod virtio;
@ -28,6 +30,8 @@ pub use self::pci::{
PciDeviceError, PciInterruptPin, PciRoot, PciVirtualConfigMmio, StubPciDevice,
StubPciParameters,
};
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
pub use self::pit::{Pit, PitError};
#[cfg(all(feature = "tpm", feature = "chromeos", target_arch = "x86_64"))]
pub use self::vtpm_proxy::VtpmProxy;
@ -41,8 +45,6 @@ cfg_if::cfg_if! {
#[cfg(feature = "direct")]
pub mod direct_irq;
mod i8042;
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
mod pit;
pub mod pl030;
mod platform;
mod proxy;
@ -80,8 +82,6 @@ cfg_if::cfg_if! {
PvPanicCode, PcieRootPort, PcieHostPort,
PvPanicPciDevice, VfioPciDevice, PciBridge,
};
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
pub use self::pit::{Pit, PitError};
pub use self::pl030::Pl030;
pub use self::platform::VfioPlatformDevice;
pub use self::proxy::Error as ProxyError;

View file

@ -435,21 +435,6 @@ fn adjust_count(count: u32) -> u32 {
}
}
/// Get the current monotonic time of host in nanoseconds
fn get_monotonic_time() -> u64 {
let mut time = libc::timespec {
tv_sec: 0,
tv_nsec: 0,
};
// Safe because time struct is local to this function and we check the returncode
let ret = unsafe { libc::clock_gettime(libc::CLOCK_MONOTONIC, &mut time) };
if ret != 0 {
0
} else {
time.tv_sec as u64 * 1_000_000_000u64 + time.tv_nsec as u64
}
}
impl PitCounter {
fn new(
counter_id: usize,
@ -485,11 +470,8 @@ impl PitCounter {
}
fn get_channel_state(&self) -> PitChannelState {
// Crosvm Pit stores start as an Instant. We do our best to convert to the host's
// monotonic clock by taking the current monotonic time and subtracting the elapsed
// time since self.start.
let load_time = match &self.start {
Some(t) => get_monotonic_time() - t.elapsed().as_nanos() as u64,
Some(t) => t.saturating_duration_since(&self.creation_time).as_nanos() as u64,
None => 0,
};
@ -566,16 +548,9 @@ impl PitCounter {
self.read_low_byte = state.read_state == PitRWState::Word1;
self.wrote_low_byte = state.write_state == PitRWState::Word1;
// To convert the count_load_time to an instant we have to convert it to a
// duration by comparing it to get_monotonic_time. Then subtract that duration from
// a "now" instant.
self.start = self
.clock
.lock()
.now()
.checked_sub(std::time::Duration::from_nanos(
get_monotonic_time() - state.count_load_time,
));
.creation_time
.checked_add(Duration::from_nanos(state.count_load_time));
}
fn get_access_mode(&self) -> Option<CommandAccess> {