diff --git a/Cargo.lock b/Cargo.lock index 7ec8aba673b..ded77945236 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -610,7 +610,6 @@ dependencies = [ "serde_derive", "serde_json", "thiserror 2.0.16", - "timerfd", "userfaultfd", "utils", "vmm", @@ -1405,15 +1404,6 @@ dependencies = [ "syn", ] -[[package]] -name = "timerfd" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84e482e368cf7efa2c8b570f476e5b9fd9fd5e9b9219fc567832b05f13511091" -dependencies = [ - "rustix", -] - [[package]] name = "tinytemplate" version = "1.2.1" @@ -1666,7 +1656,6 @@ dependencies = [ "serde_json", "slab", "thiserror 2.0.16", - "timerfd", "userfaultfd", "utils", "uuid", @@ -1683,8 +1672,7 @@ dependencies = [ [[package]] name = "vmm-sys-util" version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d21f366bf22bfba3e868349978766a965cbe628c323d58e026be80b8357ab789" +source = "git+https://github.com/roypat/vmm-sys-util?branch=timerfd-nonblock-0.14#6d30534d393c2e4f5f60ded8ba82cafac5c4fb5a" dependencies = [ "bitflags 1.3.2", "libc", diff --git a/Cargo.toml b/Cargo.toml index a1c9ad79621..a15b13602c5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,3 +41,6 @@ strip = "none" [profile.bench] strip = "debuginfo" + +[patch.crates-io] +vmm-sys-util = { git = "https://github.com/roypat/vmm-sys-util", branch = "timerfd-nonblock-0.14" } diff --git a/src/firecracker/Cargo.toml b/src/firecracker/Cargo.toml index c83ea50266a..c3cef86f10c 100644 --- a/src/firecracker/Cargo.toml +++ b/src/firecracker/Cargo.toml @@ -29,7 +29,6 @@ serde = { version = "1.0.219", features = ["derive"] } serde_derive = "1.0.136" serde_json = "1.0.143" thiserror = "2.0.16" -timerfd = "1.6.0" utils = { path = "../utils" } vmm = { path = "../vmm" } vmm-sys-util = { version = "0.14.0", features = ["with-serde"] } diff --git a/src/firecracker/src/metrics.rs b/src/firecracker/src/metrics.rs index 80f21257107..8c2843bf16b 100644 --- a/src/firecracker/src/metrics.rs +++ b/src/firecracker/src/metrics.rs @@ -5,9 +5,9 @@ use std::os::unix::io::AsRawFd; use std::time::Duration; use event_manager::{EventOps, Events, MutEventSubscriber}; -use timerfd::{ClockId, SetTimeFlags, TimerFd, TimerState}; use vmm::logger::{IncMetric, METRICS, error, warn}; use vmm_sys_util::epoll::EventSet; +use vmm_sys_util::timerfd::{TimerFd, TimerFdFlag}; /// Metrics reporting period. pub(crate) const WRITE_METRICS_PERIOD_MS: u64 = 60000; @@ -23,7 +23,7 @@ pub(crate) struct PeriodicMetrics { impl PeriodicMetrics { /// PeriodicMetrics constructor. Can panic on `TimerFd` creation failure. pub fn new() -> Self { - let write_metrics_event_fd = TimerFd::new_custom(ClockId::Monotonic, true, true) + let write_metrics_event_fd = TimerFd::new_with_flags(TimerFdFlag::NONBLOCK) .expect("Cannot create the metrics timer fd."); PeriodicMetrics { write_metrics_event_fd, @@ -35,12 +35,10 @@ impl PeriodicMetrics { /// Start the periodic metrics engine which will flush metrics every `interval_ms` millisecs. pub(crate) fn start(&mut self, interval_ms: u64) { // Arm the log write timer. - let timer_state = TimerState::Periodic { - current: Duration::from_millis(interval_ms), - interval: Duration::from_millis(interval_ms), - }; + let duration = Duration::from_millis(interval_ms); self.write_metrics_event_fd - .set_state(timer_state, SetTimeFlags::Default); + .reset(duration, Some(duration)) + .expect("failed to arm metrics write timer"); // Write the metrics straight away to check the process startup time. self.write_metrics(); @@ -77,7 +75,9 @@ impl MutEventSubscriber for PeriodicMetrics { } if source == self.write_metrics_event_fd.as_raw_fd() { - self.write_metrics_event_fd.read(); + self.write_metrics_event_fd + .wait() + .expect("failed to read metrics timer"); self.write_metrics(); } else { error!("Spurious METRICS event!"); diff --git a/src/vmm/Cargo.toml b/src/vmm/Cargo.toml index 2588c52c1be..ae4b64513cc 100644 --- a/src/vmm/Cargo.toml +++ b/src/vmm/Cargo.toml @@ -47,7 +47,6 @@ serde = { version = "1.0.219", features = ["derive", "rc"] } serde_json = "1.0.143" slab = "0.4.11" thiserror = "2.0.16" -timerfd = "1.5.0" userfaultfd = "0.9.0" utils = { path = "../utils" } uuid = "1.18.0" diff --git a/src/vmm/src/devices/virtio/balloon/device.rs b/src/vmm/src/devices/virtio/balloon/device.rs index 87a82c4fa9d..d5d03110fe9 100644 --- a/src/vmm/src/devices/virtio/balloon/device.rs +++ b/src/vmm/src/devices/virtio/balloon/device.rs @@ -7,8 +7,8 @@ use std::time::Duration; use log::{error, info}; use serde::Serialize; -use timerfd::{ClockId, SetTimeFlags, TimerFd, TimerState}; use vmm_sys_util::eventfd::EventFd; +use vmm_sys_util::timerfd::{TimerFd, TimerFdFlag}; use super::super::ActivateError; use super::super::device::{DeviceState, VirtioDevice}; @@ -215,7 +215,7 @@ impl Balloon { } let stats_timer = - TimerFd::new_custom(ClockId::Monotonic, true, true).map_err(BalloonError::Timer)?; + TimerFd::new_with_flags(TimerFdFlag::NONBLOCK).map_err(BalloonError::Timer)?; Ok(Balloon { avail_features, @@ -259,7 +259,7 @@ impl Balloon { } pub(crate) fn process_stats_timer_event(&mut self) -> Result<(), BalloonError> { - self.stats_timer.read(); + self.stats_timer.wait()?; self.trigger_stats_update() } @@ -486,12 +486,8 @@ impl Balloon { } pub fn update_timer_state(&mut self) { - let timer_state = TimerState::Periodic { - current: Duration::from_secs(u64::from(self.stats_polling_interval_s)), - interval: Duration::from_secs(u64::from(self.stats_polling_interval_s)), - }; - self.stats_timer - .set_state(timer_state, SetTimeFlags::Default); + let duration = Duration::from_secs(self.stats_polling_interval_s as u64); + self.stats_timer.reset(duration, Some(duration)); } /// Obtain the number of 4K pages the device is currently holding. diff --git a/src/vmm/src/devices/virtio/balloon/mod.rs b/src/vmm/src/devices/virtio/balloon/mod.rs index 91de01a8393..4958ae6f395 100644 --- a/src/vmm/src/devices/virtio/balloon/mod.rs +++ b/src/vmm/src/devices/virtio/balloon/mod.rs @@ -90,7 +90,7 @@ pub enum BalloonError { /// {0} InvalidAvailIdx(#[from] InvalidAvailIdx), /// Error creating the statistics timer: {0} - Timer(std::io::Error), + Timer(#[from] vmm_sys_util::errno::Error), } #[derive(Debug, thiserror::Error, displaydoc::Display)] diff --git a/src/vmm/src/devices/virtio/balloon/persist.rs b/src/vmm/src/devices/virtio/balloon/persist.rs index c5fa227c620..387111b63d0 100644 --- a/src/vmm/src/devices/virtio/balloon/persist.rs +++ b/src/vmm/src/devices/virtio/balloon/persist.rs @@ -7,7 +7,6 @@ use std::sync::Arc; use std::time::Duration; use serde::{Deserialize, Serialize}; -use timerfd::{SetTimeFlags, TimerState}; use super::*; use crate::devices::virtio::balloon::device::{BalloonStats, ConfigSpace}; @@ -157,13 +156,8 @@ impl Persist<'_> for Balloon { balloon.set_stats_desc_index(state.stats_desc_index); // Restart timer if needed. - let timer_state = TimerState::Periodic { - current: Duration::from_secs(u64::from(state.stats_polling_interval_s)), - interval: Duration::from_secs(u64::from(state.stats_polling_interval_s)), - }; - balloon - .stats_timer - .set_state(timer_state, SetTimeFlags::Default); + let duration = Duration::from_secs(state.stats_polling_interval_s as u64); + balloon.stats_timer.reset(duration, Some(duration)); } Ok(balloon) diff --git a/src/vmm/src/rate_limiter/mod.rs b/src/vmm/src/rate_limiter/mod.rs index 97ebad51fcc..670bc91cee3 100644 --- a/src/vmm/src/rate_limiter/mod.rs +++ b/src/vmm/src/rate_limiter/mod.rs @@ -5,21 +5,21 @@ use std::os::unix::io::{AsRawFd, RawFd}; use std::time::{Duration, Instant}; use std::{fmt, io}; -use timerfd::{ClockId, SetTimeFlags, TimerFd, TimerState}; +use vmm_sys_util::timerfd::{TimerFd, TimerFdFlag}; pub mod persist; #[derive(Debug, thiserror::Error, displaydoc::Display)] /// Describes the errors that may occur while handling rate limiter events. pub enum RateLimiterError { - /// The event handler was called spuriously: {0} - SpuriousRateLimiterEvent(&'static str), + /// Rate limiter event handler called without a present timer + SpuriousRateLimiterEvent, + /// Error reading timerfd: {0} + Read(#[from] vmm_sys_util::errno::Error), } // Interval at which the refill timer will run when limiter is at capacity. -const REFILL_TIMER_INTERVAL_MS: u64 = 100; -const TIMER_REFILL_STATE: TimerState = - TimerState::Oneshot(Duration::from_millis(REFILL_TIMER_INTERVAL_MS)); +const REFILL_TIMER_DURATION: Duration = Duration::from_millis(100); const NANOSEC_IN_ONE_MILLISEC: u64 = 1_000_000; @@ -367,7 +367,7 @@ impl RateLimiter { // We'll need a timer_fd, even if our current config effectively disables rate limiting, // because `Self::update_buckets()` might re-enable it later, and we might be // seccomp-blocked from creating the timer_fd at that time. - let timer_fd = TimerFd::new_custom(ClockId::Monotonic, true, true)?; + let timer_fd = TimerFd::new_with_flags(TimerFdFlag::NONBLOCK)?; Ok(RateLimiter { bandwidth: bytes_token_bucket, @@ -378,9 +378,11 @@ impl RateLimiter { } // Arm the timer of the rate limiter with the provided `TimerState`. - fn activate_timer(&mut self, timer_state: TimerState) { + fn activate_timer(&mut self, one_shot_duration: Duration) { // Register the timer; don't care about its previous state - self.timer_fd.set_state(timer_state, SetTimeFlags::Default); + self.timer_fd + .reset(one_shot_duration, None) + .expect("failed to activate ratelimiter timer"); self.timer_active = true; } @@ -407,7 +409,7 @@ impl RateLimiter { // make sure there is only one running timer for this limiter. BucketReduction::Failure => { if !self.timer_active { - self.activate_timer(TIMER_REFILL_STATE); + self.activate_timer(REFILL_TIMER_DURATION); } false } @@ -424,9 +426,7 @@ impl RateLimiter { // `ratio * refill_time` milliseconds. // The conversion should be safe because the ratio is positive. #[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)] - self.activate_timer(TimerState::Oneshot(Duration::from_millis( - (ratio * refill_time as f64) as u64, - ))); + self.activate_timer(Duration::from_millis((ratio * refill_time as f64) as u64)); true } } @@ -469,10 +469,8 @@ impl RateLimiter { /// /// If the rate limiter is disabled or is not blocked, an error is returned. pub fn event_handler(&mut self) -> Result<(), RateLimiterError> { - match self.timer_fd.read() { - 0 => Err(RateLimiterError::SpuriousRateLimiterEvent( - "Rate limiter event handler called without a present timer", - )), + match self.timer_fd.wait()? { + 0 => Err(RateLimiterError::SpuriousRateLimiterEvent), _ => { self.timer_active = false; Ok(()) @@ -779,7 +777,7 @@ pub(crate) mod tests { // second wait will always result in the limiter being refilled. Otherwise // there is a chance for a race condition between limiter refilling and limiter // checking. - const TEST_REFILL_TIMER_INTERVAL_MS: u64 = REFILL_TIMER_INTERVAL_MS + 10; + const TEST_REFILL_TIMER_DURATION: Duration = Duration::from_millis(110); impl TokenBucket { // Resets the token bucket: budget set to max capacity and last-updated set to now. @@ -950,11 +948,11 @@ pub(crate) mod tests { assert!(l.consume(u64::MAX, TokenType::Ops)); assert!(l.consume(u64::MAX, TokenType::Bytes)); // calling the handler without there having been an event should error - l.event_handler().unwrap_err(); - assert_eq!( - format!("{:?}", l.event_handler().err().unwrap()), - "SpuriousRateLimiterEvent(\"Rate limiter event handler called without a present \ - timer\")" + let err = l.event_handler().unwrap_err(); + assert!( + matches!(err, RateLimiterError::SpuriousRateLimiterEvent), + "{:?}", + err ); } @@ -1016,11 +1014,11 @@ pub(crate) mod tests { // since consume failed, limiter should be blocked now assert!(l.is_blocked()); // wait half the timer period - thread::sleep(Duration::from_millis(TEST_REFILL_TIMER_INTERVAL_MS / 2)); + thread::sleep(TEST_REFILL_TIMER_DURATION / 2); // limiter should still be blocked assert!(l.is_blocked()); // wait the other half of the timer period - thread::sleep(Duration::from_millis(TEST_REFILL_TIMER_INTERVAL_MS / 2)); + thread::sleep(TEST_REFILL_TIMER_DURATION / 2); // the timer_fd should have an event on it by now l.event_handler().unwrap(); // limiter should now be unblocked @@ -1049,11 +1047,11 @@ pub(crate) mod tests { // since consume failed, limiter should be blocked now assert!(l.is_blocked()); // wait half the timer period - thread::sleep(Duration::from_millis(TEST_REFILL_TIMER_INTERVAL_MS / 2)); + thread::sleep(TEST_REFILL_TIMER_DURATION / 2); // limiter should still be blocked assert!(l.is_blocked()); // wait the other half of the timer period - thread::sleep(Duration::from_millis(TEST_REFILL_TIMER_INTERVAL_MS / 2)); + thread::sleep(TEST_REFILL_TIMER_DURATION / 2); // the timer_fd should have an event on it by now l.event_handler().unwrap(); // limiter should now be unblocked @@ -1083,11 +1081,11 @@ pub(crate) mod tests { // since consume failed, limiter should be blocked now assert!(l.is_blocked()); // wait half the timer period - thread::sleep(Duration::from_millis(TEST_REFILL_TIMER_INTERVAL_MS / 2)); + thread::sleep(TEST_REFILL_TIMER_DURATION / 2); // limiter should still be blocked assert!(l.is_blocked()); // wait the other half of the timer period - thread::sleep(Duration::from_millis(TEST_REFILL_TIMER_INTERVAL_MS / 2)); + thread::sleep(TEST_REFILL_TIMER_DURATION / 2); // the timer_fd should have an event on it by now l.event_handler().unwrap(); // limiter should now be unblocked diff --git a/src/vmm/src/rate_limiter/persist.rs b/src/vmm/src/rate_limiter/persist.rs index 6c9e5052ecf..12305cd313d 100644 --- a/src/vmm/src/rate_limiter/persist.rs +++ b/src/vmm/src/rate_limiter/persist.rs @@ -4,6 +4,7 @@ //! Defines the structures needed for saving/restoring a RateLimiter. use serde::{Deserialize, Serialize}; +use vmm_sys_util::timerfd::{TimerFd, TimerFdFlag}; use super::*; use crate::snapshot::Persist; @@ -82,7 +83,7 @@ impl Persist<'_> for RateLimiter { } else { None }, - timer_fd: TimerFd::new_custom(ClockId::Monotonic, true, true)?, + timer_fd: TimerFd::new_with_flags(TimerFdFlag::NONBLOCK)?, timer_active: false, }; @@ -151,10 +152,7 @@ mod tests { .unwrap() .partial_eq(restored_rate_limiter.bandwidth().unwrap()) ); - assert_eq!( - restored_rate_limiter.timer_fd.get_state(), - TimerState::Disarmed - ); + assert!(!restored_rate_limiter.timer_fd.is_armed().unwrap()); // Check that RateLimiter restores correctly after partially consuming tokens. rate_limiter.consume(10, TokenType::Bytes); @@ -174,10 +172,7 @@ mod tests { .unwrap() .partial_eq(restored_rate_limiter.bandwidth().unwrap()) ); - assert_eq!( - restored_rate_limiter.timer_fd.get_state(), - TimerState::Disarmed - ); + assert!(!restored_rate_limiter.timer_fd.is_armed().unwrap()); // Check that RateLimiter restores correctly after totally consuming tokens. rate_limiter.consume(1000, TokenType::Bytes);