diff --git a/Cargo.lock b/Cargo.lock index 7362fd41e8f..4bbce0a3784 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -285,11 +285,11 @@ name = "devices" version = "0.1.0" dependencies = [ "dumbo", + "event-manager", "libc", "logger", "mmds", "net_gen", - "polly", "proptest", "rate_limiter", "serde", @@ -319,15 +319,25 @@ version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" +[[package]] +name = "event-manager" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "377fa591135fbe23396a18e2655a6d5481bf7c5823cdfa3cc81b01a229cbe640" +dependencies = [ + "libc", + "vmm-sys-util", +] + [[package]] name = "firecracker" version = "0.24.0" dependencies = [ "api_server", + "event-manager", "libc", "logger", "mmds", - "polly", "seccompiler", "snapshot", "timerfd", @@ -542,14 +552,6 @@ dependencies = [ "web-sys", ] -[[package]] -name = "polly" -version = "0.0.1" -dependencies = [ - "libc", - "utils", -] - [[package]] name = "ppv-lite86" version = "0.2.10" @@ -1001,6 +1003,7 @@ dependencies = [ "cpuid", "criterion", "devices", + "event-manager", "kernel", "kvm-bindings", "kvm-ioctls", @@ -1008,7 +1011,6 @@ dependencies = [ "libc", "logger", "mmds", - "polly", "rate_limiter", "seccompiler", "serde", diff --git a/src/devices/Cargo.toml b/src/devices/Cargo.toml index d0548f786df..1a3cb1d19c8 100644 --- a/src/devices/Cargo.toml +++ b/src/devices/Cargo.toml @@ -5,6 +5,7 @@ authors = ["The Chromium OS Authors"] edition = "2018" [dependencies] +event-manager = ">=0.2.1" libc = ">=0.2.39" timerfd = ">=1.0" versionize = ">=0.1.6" @@ -15,7 +16,6 @@ dumbo = { path = "../dumbo" } logger = { path = "../logger" } mmds = { path = "../mmds" } net_gen = { path = "../net_gen" } -polly = { path = "../polly" } rate_limiter = { path = "../rate_limiter" } serde = { version = ">=1.0.27", features = ["derive"] } snapshot = { path = "../snapshot" } diff --git a/src/devices/src/legacy/serial.rs b/src/devices/src/legacy/serial.rs index c6810e2febd..c55c569d30f 100644 --- a/src/devices/src/legacy/serial.rs +++ b/src/devices/src/legacy/serial.rs @@ -9,9 +9,10 @@ use std::collections::VecDeque; use std::io; use std::os::unix::io::{AsRawFd, RawFd}; +use event_manager::{EventOps, Events, MutEventSubscriber}; + use logger::{error, warn, IncMetric, METRICS}; -use polly::event_manager::{EventManager, Pollable, Subscriber}; -use utils::epoll::{EpollEvent, EventSet}; +use utils::epoll::EventSet; use utils::eventfd::EventFd; use crate::bus::BusDevice; @@ -327,42 +328,31 @@ impl Serial { .map_or(Ok(()), |buf_ready| buf_ready.write(1)) } - fn handle_ewouldblock(&self, ev_mgr: &mut EventManager) { + fn handle_ewouldblock(&self, ops: &mut EventOps) { let buffer_ready_fd = self.buffer_ready_evt_fd(); let input_fd = self.serial_input_fd(); if input_fd < 0 || buffer_ready_fd < 0 { error!("Serial does not have a configured input source."); return; } - - if ev_mgr.subscriber(input_fd).is_err() { - match ev_mgr.subscriber(buffer_ready_fd) { - Ok(serial) => { - match ev_mgr.register( - input_fd, - EpollEvent::new(EventSet::IN, input_fd as u64), - serial.clone(), - ) { - // Bytes might had come on the unregistered stdin. Try to consume any. - Ok(_) => self.signal_buffer_ready().unwrap_or_else(|err| { - error!( - "Could not signal that serial device buffer is ready: {:?}", - err - ) - }), - Err(e) => { - error!( - "Could not register the serial input to the event manager: {:?}", - e - ); - } - } - } - Err(e) => { - error!("Could not get the serial device subscriber: {:?}", e); - } + match ops.add(Events::new(&input_fd, EventSet::IN)) { + Err(event_manager::Error::FdAlreadyRegistered) => (), + Err(e) => { + error!( + "Could not register the serial input to the event manager: {:?}", + e + ); } - } + Ok(()) => { + // Bytes might had come on the unregistered stdin. Try to consume any. + self.signal_buffer_ready().unwrap_or_else(|err| { + error!( + "Could not signal that serial device buffer is ready: {:?}", + err + ) + }) + } + }; } } @@ -400,14 +390,14 @@ impl BusDevice for Serial { } } -impl Subscriber for Serial { +impl MutEventSubscriber for Serial { /// Handle events on the serial input fd. - fn process(&mut self, event: &EpollEvent, ev_mgr: &mut EventManager) { + fn process(&mut self, event: Events, ops: &mut EventOps) { #[inline] - fn unregister_source(ev_mgr: &mut EventManager, source: Pollable) { - match ev_mgr.unregister(source) { + fn unregister_source(ops: &mut EventOps, source: &T) { + match ops.remove(Events::new(source, EventSet::IN)) { Ok(_) => (), - Err(_) => error!("Could not unregister the source: {}", source), + Err(_) => error!("Could not unregister source fd: {}", source.as_raw_fd()), } } @@ -423,8 +413,8 @@ impl Subscriber for Serial { Ok(_) => (), Err(err) => { error!("Detach serial device input source due to error in consuming the buffer ready event: {:?}", err); - unregister_source(ev_mgr, input_fd); - unregister_source(ev_mgr, buffer_ready_fd); + unregister_source(ops, &input_fd); + unregister_source(ops, &buffer_ready_fd); return; } } @@ -437,28 +427,28 @@ impl Subscriber for Serial { Ok(count) => { // Handle EOF if the event came from the input source. if input_fd == event.fd() && count == 0 { - unregister_source(ev_mgr, input_fd); - unregister_source(ev_mgr, buffer_ready_fd); + unregister_source(ops, &input_fd); + unregister_source(ops, &buffer_ready_fd); warn!("Detached the serial input due to peer close/error."); } } Err(e) => { match e.raw_os_error() { Some(errno) if errno == libc::ENOBUFS => { - unregister_source(ev_mgr, input_fd); + unregister_source(ops, &input_fd); } Some(errno) if errno == libc::EWOULDBLOCK => { - self.handle_ewouldblock(ev_mgr); + self.handle_ewouldblock(ops); } Some(errno) if errno == libc::ENOTTY => { error!("The serial device does not have the input source attached."); - unregister_source(ev_mgr, input_fd); - unregister_source(ev_mgr, buffer_ready_fd); + unregister_source(ops, &input_fd); + unregister_source(ops, &buffer_ready_fd); } Some(_) | None => { // Unknown error, detach the serial input source. - unregister_source(ev_mgr, input_fd); - unregister_source(ev_mgr, buffer_ready_fd); + unregister_source(ops, &input_fd); + unregister_source(ops, &buffer_ready_fd); warn!("Detached the serial input due to peer close/error."); } } @@ -468,16 +458,16 @@ impl Subscriber for Serial { /// Initial registration of pollable objects. /// If serial input is present, register the serial input FD as readable. - fn interest_list(&self) -> Vec { - match &self.input { - Some(input) => match self.buffer_ready_evt.as_ref() { - Some(buf_ready_evt) => vec![ - EpollEvent::new(EventSet::IN, input.as_raw_fd() as u64), - EpollEvent::new(EventSet::IN, buf_ready_evt.as_raw_fd() as u64), - ], - None => vec![], - }, - None => vec![], + fn init(&mut self, ops: &mut EventOps) { + if self.input.is_some() { + if let Some(buf_ready_evt) = self.buffer_ready_evt.as_ref() { + if let Err(e) = ops.add(Events::new(&self.serial_input_fd(), EventSet::IN)) { + error!("Failed to register serial input fd: {}", e); + } + if let Err(e) = ops.add(Events::new(buf_ready_evt, EventSet::IN)) { + error!("Failed to register serial buffer ready event: {}", e); + } + } } } } @@ -490,7 +480,7 @@ mod tests { use std::os::unix::io::RawFd; use std::sync::{Arc, Mutex}; - use polly::event_manager::EventManager; + use event_manager::{EventManager, SubscriberOps}; struct SharedBufferInternal { read_buf: Vec, @@ -578,107 +568,6 @@ mod tests { static RAW_INPUT_BUF: [u8; 3] = [b'a', b'b', b'c']; - #[test] - fn test_event_handling_no_in() { - let mut event_manager = EventManager::new().unwrap(); - - let intr_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap(); - let serial_out = SharedBuffer::new(); - - let mut serial = Serial::new_out(intr_evt, Box::new(serial_out)); - // A serial without in does not have any events in the list. - - assert!(serial.interest_list().is_empty()); - // Even though there is no in or hangup, process should not panic. Call it to validate this. - let epoll_event = EpollEvent::new(EventSet::IN, 0); - serial.process(&epoll_event, &mut event_manager); - } - - #[test] - fn test_event_handling_with_in() { - let mut event_manager = EventManager::new().unwrap(); - - let intr_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap(); - let serial_in_out = SharedBuffer::new(); - - let mut serial = Serial::new_in_out( - intr_evt.try_clone().unwrap(), - Box::new(serial_in_out.clone()), - Box::new(serial_in_out), - Some(EventFd::new(libc::EFD_NONBLOCK).unwrap()), - ); - // Check that the interest list contains one event set. - assert_eq!(serial.interest_list().len(), 2); - - // Process an invalid event type does not panic. - let invalid_event = EpollEvent::new(EventSet::OUT, intr_evt.as_raw_fd() as u64); - serial.process(&invalid_event, &mut event_manager); - - // Process an event with a `RawFd` that does not correspond to `intr_evt` does not panic. - let invalid_event = EpollEvent::new(EventSet::IN, 0); - serial.process(&invalid_event, &mut event_manager); - } - - #[test] - fn test_event_handling_ewould_block() { - let mut event_manager = EventManager::new().unwrap(); - - let intr_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap(); - let serial_in_out = SharedBuffer::new(); - - let mut serial = Serial::new_in_out( - intr_evt.try_clone().unwrap(), - Box::new(serial_in_out.clone()), - Box::new(serial_in_out.clone()), - Some(EventFd::new(libc::EFD_NONBLOCK).unwrap()), - ); - - // Process a spurious event, which will result in EWOULDBLOCK and unregister the serial input. - let spurious_ev = EpollEvent::new(EventSet::IN, serial_in_out.as_raw_fd() as u64); - serial.process(&spurious_ev, &mut event_manager); - - // Try to modify the input event. Will result in Error since the serial input was unregistered. - event_manager - .modify(serial_in_out.as_raw_fd(), spurious_ev) - .unwrap_err(); - } - - #[test] - fn test_event_handling_err_and_hup() { - let mut event_manager = EventManager::new().unwrap(); - let serial_in_out = SharedBuffer::new(); - let mut serial = Serial::new_in_out( - EventFd::new(libc::EFD_NONBLOCK).unwrap(), - Box::new(serial_in_out.clone()), - Box::new(serial_in_out.clone()), - Some(EventFd::new(libc::EFD_NONBLOCK).unwrap()), - ); - - // Check that the interest list contains one event set. - let expected_medium_bytes = [b'a'; FIFO_SIZE]; - assert_eq!(serial.interest_list().len(), 2); - { - let mut guard = serial_in_out.internal.lock().unwrap(); - guard.read_buf.write_all(&expected_medium_bytes).unwrap(); - } - - assert!(serial.in_buffer.is_empty()); - let err_hup_ev = EpollEvent::new( - EventSet::ERROR | EventSet::HANG_UP, - serial_in_out.as_raw_fd() as u64, - ); - - serial.process(&err_hup_ev, &mut event_manager); - assert_eq!(serial.in_buffer.len(), expected_medium_bytes.len()); - serial.in_buffer.clear(); - - // Process one more round of `EventSet::HANG_UP`. - // Check that the processing does not bring anything new to the serial - // `in_buffer`. - serial.process(&err_hup_ev, &mut event_manager); - assert!(serial.in_buffer.is_empty()); - } - #[test] fn test_serial_output() { let intr_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap(); @@ -760,7 +649,7 @@ mod tests { let mut evmgr = EventManager::new().unwrap(); let serial_wrap = Arc::new(Mutex::new(serial)); - evmgr.add_subscriber(serial_wrap.clone()).unwrap(); + let _id = evmgr.add_subscriber(serial_wrap.clone()); // Run the event handler which should drive serial input. // There should be one event reported (which should have also handled serial input). diff --git a/src/devices/src/virtio/balloon/device.rs b/src/devices/src/virtio/balloon/device.rs index 64036242352..58f8ecd5f6f 100644 --- a/src/devices/src/virtio/balloon/device.rs +++ b/src/devices/src/virtio/balloon/device.rs @@ -598,7 +598,6 @@ impl VirtioDevice for Balloon { #[cfg(test)] pub(crate) mod tests { - use std::os::unix::io::AsRawFd; use std::u32; use super::super::CONFIG_SPACE_SIZE; @@ -609,8 +608,6 @@ pub(crate) mod tests { }; use crate::virtio::test_utils::{default_mem, VirtQueue}; use crate::virtio::{VIRTQ_DESC_F_NEXT, VIRTQ_DESC_F_WRITE}; - use ::utils::epoll::{EpollEvent, EventSet}; - use polly::event_manager::{EventManager, Subscriber}; use vm_memory::GuestAddress; impl Balloon { @@ -837,12 +834,6 @@ pub(crate) mod tests { balloon.set_queue(INFLATE_INDEX, infq.create_queue()); balloon.activate(mem.clone()).unwrap(); - let mut event_manager = EventManager::new().unwrap(); - let queue_evt = EpollEvent::new( - EventSet::IN, - balloon.queue_evts[INFLATE_INDEX].as_raw_fd() as u64, - ); - // Fill the third page with non-zero bytes. for i in 0..0x1000 { assert!(mem.write_obj::(1, GuestAddress((1 << 12) + i)).is_ok()); @@ -861,7 +852,9 @@ pub(crate) mod tests { check_metric_after_block!( METRICS.balloon.event_fails, 1, - balloon.process(&queue_evt, &mut event_manager) + balloon + .process_inflate_queue_event() + .unwrap_or_else(report_balloon_event_fail) ); // Verify that nothing got processed. assert_eq!(infq.used.idx.get(), 0); @@ -899,12 +892,6 @@ pub(crate) mod tests { balloon.set_queue(DEFLATE_INDEX, defq.create_queue()); balloon.activate(mem.clone()).unwrap(); - let mut event_manager = EventManager::new().unwrap(); - let queue_evt = EpollEvent::new( - EventSet::IN, - balloon.queue_evts[DEFLATE_INDEX].as_raw_fd() as u64, - ); - let page_addr = 0x10; // Error case: forgot to trigger deflate event queue. @@ -913,7 +900,9 @@ pub(crate) mod tests { check_metric_after_block!( METRICS.balloon.event_fails, 1, - balloon.process(&queue_evt, &mut event_manager) + balloon + .process_deflate_queue_event() + .unwrap_or_else(report_balloon_event_fail) ); // Verify that nothing got processed. assert_eq!(defq.used.idx.get(), 0); @@ -939,12 +928,6 @@ pub(crate) mod tests { balloon.set_queue(STATS_INDEX, statsq.create_queue()); balloon.activate(mem.clone()).unwrap(); - let mut event_manager = EventManager::new().unwrap(); - let queue_evt = EpollEvent::new( - EventSet::IN, - balloon.queue_evts[STATS_INDEX].as_raw_fd() as u64, - ); - let page_addr = 0x100; // Error case: forgot to trigger stats event queue. @@ -953,7 +936,9 @@ pub(crate) mod tests { check_metric_after_block!( METRICS.balloon.event_fails, 1, - balloon.process(&queue_evt, &mut event_manager) + balloon + .process_stats_queue_event() + .unwrap_or_else(report_balloon_event_fail) ); // Verify that nothing got processed. assert_eq!(statsq.used.idx.get(), 0); @@ -989,7 +974,7 @@ pub(crate) mod tests { check_metric_after_block!(METRICS.balloon.stats_updates_count, 1, { // Trigger the queue event. balloon.queue_events()[STATS_INDEX].write(1).unwrap(); - balloon.process(&queue_evt, &mut event_manager); + balloon.process_stats_queue_event().unwrap(); // Don't check for completion yet. }); diff --git a/src/devices/src/virtio/balloon/event_handler.rs b/src/devices/src/virtio/balloon/event_handler.rs index 9f60989fbbb..4af5841b2e4 100644 --- a/src/devices/src/virtio/balloon/event_handler.rs +++ b/src/devices/src/virtio/balloon/event_handler.rs @@ -3,9 +3,9 @@ use std::os::unix::io::AsRawFd; +use event_manager::{EventOps, Events, MutEventSubscriber}; use logger::{debug, error, warn}; -use polly::event_manager::{EventManager, Subscriber}; -use utils::epoll::{EpollEvent, EventSet}; +use utils::epoll::EventSet; use crate::report_balloon_event_fail; use crate::virtio::{ @@ -13,40 +13,43 @@ use crate::virtio::{ }; impl Balloon { - fn process_activate_event(&self, event_manager: &mut EventManager) { + fn register_runtime_events(&self, ops: &mut EventOps) { + if let Err(e) = ops.add(Events::new(&self.queue_evts[INFLATE_INDEX], EventSet::IN)) { + error!("Failed to register inflate queue event: {}", e); + } + if let Err(e) = ops.add(Events::new(&self.queue_evts[DEFLATE_INDEX], EventSet::IN)) { + error!("Failed to register deflate queue event: {}", e); + } + if self.stats_enabled() { + if let Err(e) = ops.add(Events::new(&self.queue_evts[STATS_INDEX], EventSet::IN)) { + error!("Failed to register stats queue event: {}", e); + } + if let Err(e) = ops.add(Events::new(&self.stats_timer, EventSet::IN)) { + error!("Failed to register stats timerfd event: {}", e); + } + } + } + + fn register_activate_event(&self, ops: &mut EventOps) { + if let Err(e) = ops.add(Events::new(&self.activate_evt, EventSet::IN)) { + error!("Failed to register activate event: {}", e); + } + } + + fn process_activate_event(&self, ops: &mut EventOps) { debug!("balloon: activate event"); if let Err(e) = self.activate_evt.read() { error!("Failed to consume balloon activate event: {:?}", e); } - let activate_fd = self.activate_evt.as_raw_fd(); - // The subscriber must exist as we previously registered activate_evt via - // `interest_list()`. - let self_subscriber = match event_manager.subscriber(activate_fd) { - Ok(subscriber) => subscriber, - Err(e) => { - error!("Failed to process balloon activate evt: {:?}", e); - return; - } - }; - - // Interest list changes when the device is activated. - let interest_list = self.interest_list(); - for event in interest_list { - event_manager - .register(event.data() as i32, event, self_subscriber.clone()) - .unwrap_or_else(|e| { - error!("Failed to register balloon events: {:?}", e); - }); + self.register_runtime_events(ops); + if let Err(e) = ops.remove(Events::new(&self.activate_evt, EventSet::IN)) { + error!("Failed to un-register activate event: {}", e); } - - event_manager.unregister(activate_fd).unwrap_or_else(|e| { - error!("Failed to unregister balloon activate evt: {:?}", e); - }); } } -impl Subscriber for Balloon { - fn process(&mut self, event: &EpollEvent, evmgr: &mut EventManager) { +impl MutEventSubscriber for Balloon { + fn process(&mut self, event: Events, ops: &mut EventOps) { let source = event.fd(); let event_set = event.event_set(); let supported_events = EventSet::IN; @@ -80,7 +83,7 @@ impl Subscriber for Balloon { _ if source == stats_timer_fd => self .process_stats_timer_event() .unwrap_or_else(report_balloon_event_fail), - _ if activate_fd == source => self.process_activate_event(evmgr), + _ if activate_fd == source => self.process_activate_event(ops), _ => { warn!("Balloon: Spurious event received: {:?}", source); } @@ -93,37 +96,15 @@ impl Subscriber for Balloon { } } - fn interest_list(&self) -> Vec { + fn init(&mut self, ops: &mut EventOps) { // This function can be called during different points in the device lifetime: // - shortly after device creation, // - on device activation (is-activated already true at this point), // - on device restore from snapshot. if self.is_activated() { - let mut events = vec![ - EpollEvent::new( - EventSet::IN, - self.queue_evts[INFLATE_INDEX].as_raw_fd() as u64, - ), - EpollEvent::new( - EventSet::IN, - self.queue_evts[DEFLATE_INDEX].as_raw_fd() as u64, - ), - ]; - if self.stats_enabled() { - events.extend(vec![ - EpollEvent::new( - EventSet::IN, - self.queue_evts[STATS_INDEX].as_raw_fd() as u64, - ), - EpollEvent::new(EventSet::IN, self.stats_timer.as_raw_fd() as u64), - ]); - } - events + self.register_runtime_events(ops); } else { - vec![EpollEvent::new( - EventSet::IN, - self.activate_evt.as_raw_fd() as u64, - )] + self.register_activate_event(ops); } } } @@ -135,6 +116,7 @@ pub mod tests { use super::*; use crate::virtio::balloon::test_utils::set_request; use crate::virtio::test_utils::{default_mem, VirtQueue}; + use event_manager::{EventManager, SubscriberOps}; use vm_memory::GuestAddress; #[test] @@ -146,7 +128,7 @@ pub mod tests { balloon.set_queue(INFLATE_INDEX, infq.create_queue()); let balloon = Arc::new(Mutex::new(balloon)); - event_manager.add_subscriber(balloon.clone()).unwrap(); + let _id = event_manager.add_subscriber(balloon.clone()); // Push a queue event, use the inflate queue in this test. { @@ -164,14 +146,13 @@ pub mod tests { // Manually force a queue event and check it's ignored pre-activation. { - let mut b = balloon.lock().unwrap(); - let raw_infq_evt = b.queue_evts[INFLATE_INDEX].as_raw_fd() as u64; + let b = balloon.lock().unwrap(); // Artificially push event. - b.process( - &EpollEvent::new(EventSet::IN, raw_infq_evt), - &mut event_manager, - ); + b.queue_evts[INFLATE_INDEX].write(1).unwrap(); + // Process the pushed event. + let ev_count = event_manager.run_with_timeout(50).unwrap(); // Validate there was no queue operation. + assert_eq!(ev_count, 0); assert_eq!(infq.used.idx.get(), 0); } diff --git a/src/devices/src/virtio/balloon/test_utils.rs b/src/devices/src/virtio/balloon/test_utils.rs index 5f0f0c58498..034bc2be03c 100644 --- a/src/devices/src/virtio/balloon/test_utils.rs +++ b/src/devices/src/virtio/balloon/test_utils.rs @@ -1,24 +1,22 @@ // Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -use std::os::unix::io::AsRawFd; use std::u32; -use crate::virtio::balloon::NUM_QUEUES; use crate::virtio::test_utils::VirtQueue; -use crate::virtio::Balloon; -use ::utils::epoll::{EpollEvent, EventSet}; -use polly::event_manager::{EventManager, Subscriber}; +use crate::virtio::{balloon::NUM_QUEUES, Balloon, DEFLATE_INDEX, INFLATE_INDEX, STATS_INDEX}; pub fn invoke_handler_for_queue_event(b: &mut Balloon, queue_index: usize) { assert!(queue_index < NUM_QUEUES); // Trigger the queue event. b.queue_evts[queue_index].write(1).unwrap(); // Handle event. - b.process( - &EpollEvent::new(EventSet::IN, b.queue_evts[queue_index].as_raw_fd() as u64), - &mut EventManager::new().unwrap(), - ); + match queue_index { + INFLATE_INDEX => b.process_inflate_queue_event().unwrap(), + DEFLATE_INDEX => b.process_deflate_queue_event().unwrap(), + STATS_INDEX => b.process_stats_queue_event().unwrap(), + _ => unreachable!(), + }; // Validate the queue operation finished successfully. assert_eq!(b.interrupt_evt.read().unwrap(), 1); } diff --git a/src/devices/src/virtio/block/device.rs b/src/devices/src/virtio/block/device.rs index ca3e25f81d3..5418956d848 100644 --- a/src/devices/src/virtio/block/device.rs +++ b/src/devices/src/virtio/block/device.rs @@ -527,15 +527,12 @@ impl VirtioDevice for Block { #[cfg(test)] pub(crate) mod tests { use std::fs::metadata; - use std::os::unix::io::AsRawFd; use std::thread; use std::time::Duration; use std::u32; use super::*; use crate::virtio::queue::tests::*; - use polly::event_manager::{EventManager, Subscriber}; - use utils::epoll::{EpollEvent, EventSet}; use utils::tempfile::TempFile; use vm_memory::GuestAddress; @@ -1097,16 +1094,12 @@ pub(crate) mod tests { let data_addr = GuestAddress(vq.dtable[1].addr.get()); let status_addr = GuestAddress(vq.dtable[2].addr.get()); - let mut event_manager = EventManager::new().unwrap(); - let queue_evt = EpollEvent::new(EventSet::IN, block.queue_evts[0].as_raw_fd() as u64); - // Create bandwidth rate limiter that allows only 80 bytes/s with bucket size of 8 bytes. let mut rl = RateLimiter::new(8, 0, 100, 0, 0, 0).unwrap(); // Use up the budget. assert!(rl.consume(8, TokenType::Bytes)); set_rate_limiter(&mut block, rl); - let rate_limiter_evt = EpollEvent::new(EventSet::IN, block.rate_limiter.as_raw_fd() as u64); mem.write_obj::(VIRTIO_BLK_T_OUT, request_type_addr) .unwrap(); @@ -1122,7 +1115,7 @@ pub(crate) mod tests { check_metric_after_block!( &METRICS.block.rate_limiter_throttled_events, 1, - block.process(&queue_evt, &mut event_manager) + block.process_queue_event() ); // Assert that limiter is blocked. @@ -1142,7 +1135,7 @@ pub(crate) mod tests { check_metric_after_block!( &METRICS.block.rate_limiter_throttled_events, 0, - block.process(&rate_limiter_evt, &mut event_manager) + block.process_rate_limiter_event() ); // Validate the rate_limiter is no longer blocked. assert!(!block.rate_limiter.is_blocked()); @@ -1171,16 +1164,12 @@ pub(crate) mod tests { let data_addr = GuestAddress(vq.dtable[1].addr.get()); let status_addr = GuestAddress(vq.dtable[2].addr.get()); - let mut event_manager = EventManager::new().unwrap(); - let queue_evt = EpollEvent::new(EventSet::IN, block.queue_evts[0].as_raw_fd() as u64); - // Create ops rate limiter that allows only 10 ops/s with bucket size of 1 ops. let mut rl = RateLimiter::new(0, 0, 0, 1, 0, 100).unwrap(); // Use up the budget. assert!(rl.consume(1, TokenType::Ops)); set_rate_limiter(&mut block, rl); - let rate_limiter_evt = EpollEvent::new(EventSet::IN, block.rate_limiter.as_raw_fd() as u64); mem.write_obj::(VIRTIO_BLK_T_OUT, request_type_addr) .unwrap(); @@ -1196,7 +1185,7 @@ pub(crate) mod tests { check_metric_after_block!( &METRICS.block.rate_limiter_throttled_events, 1, - block.process(&queue_evt, &mut event_manager) + block.process_queue_event() ); // Assert that limiter is blocked. @@ -1214,7 +1203,7 @@ pub(crate) mod tests { check_metric_after_block!( &METRICS.block.rate_limiter_throttled_events, 1, - block.process(&queue_evt, &mut event_manager) + block.process_queue_event() ); // Assert that limiter is blocked. @@ -1234,7 +1223,7 @@ pub(crate) mod tests { check_metric_after_block!( &METRICS.block.rate_limiter_throttled_events, 0, - block.process(&rate_limiter_evt, &mut event_manager) + block.process_rate_limiter_event() ); // Validate the rate_limiter is no longer blocked. assert!(!block.rate_limiter.is_blocked()); diff --git a/src/devices/src/virtio/block/event_handler.rs b/src/devices/src/virtio/block/event_handler.rs index 0919c2eb71c..181d5f6422b 100644 --- a/src/devices/src/virtio/block/event_handler.rs +++ b/src/devices/src/virtio/block/event_handler.rs @@ -2,49 +2,44 @@ // SPDX-License-Identifier: Apache-2.0 use std::os::unix::io::AsRawFd; +use event_manager::{EventOps, Events, MutEventSubscriber}; use logger::{debug, error, warn}; -use polly::event_manager::{EventManager, Subscriber}; -use utils::epoll::{EpollEvent, EventSet}; +use utils::epoll::EventSet; use crate::virtio::block::device::Block; use crate::virtio::VirtioDevice; impl Block { - fn process_activate_event(&self, event_manager: &mut EventManager) { + fn register_runtime_events(&self, ops: &mut EventOps) { + if let Err(e) = ops.add(Events::new(&self.queue_evts[0], EventSet::IN)) { + error!("Failed to register queue event: {}", e); + } + if let Err(e) = ops.add(Events::new(&self.rate_limiter, EventSet::IN)) { + error!("Failed to register ratelimiter event: {}", e); + } + } + + fn register_activate_event(&self, ops: &mut EventOps) { + if let Err(e) = ops.add(Events::new(&self.activate_evt, EventSet::IN)) { + error!("Failed to register activate event: {}", e); + } + } + + fn process_activate_event(&self, ops: &mut EventOps) { debug!("block: activate event"); if let Err(e) = self.activate_evt.read() { error!("Failed to consume block activate event: {:?}", e); } - let activate_fd = self.activate_evt.as_raw_fd(); - // The subscriber must exist as we previously registered activate_evt via - // `interest_list()`. - let self_subscriber = match event_manager.subscriber(activate_fd) { - Ok(subscriber) => subscriber, - Err(e) => { - error!("Failed to process block activate evt: {:?}", e); - return; - } - }; - - // Interest list changes when the device is activated. - let interest_list = self.interest_list(); - for event in interest_list { - event_manager - .register(event.data() as i32, event, self_subscriber.clone()) - .unwrap_or_else(|e| { - error!("Failed to register block events: {:?}", e); - }); + self.register_runtime_events(ops); + if let Err(e) = ops.remove(Events::new(&self.activate_evt, EventSet::IN)) { + error!("Failed to un-register activate event: {}", e); } - - event_manager.unregister(activate_fd).unwrap_or_else(|e| { - error!("Failed to unregister block activate evt: {:?}", e); - }); } } -impl Subscriber for Block { +impl MutEventSubscriber for Block { // Handle an event for queue or rate limiter. - fn process(&mut self, event: &EpollEvent, evmgr: &mut EventManager) { + fn process(&mut self, event: Events, ops: &mut EventOps) { let source = event.fd(); let event_set = event.event_set(); @@ -68,7 +63,7 @@ impl Subscriber for Block { match source { _ if queue_evt == source => self.process_queue_event(), _ if rate_limiter_evt == source => self.process_rate_limiter_event(), - _ if activate_fd == source => self.process_activate_event(evmgr), + _ if activate_fd == source => self.process_activate_event(ops), _ => warn!("Block: Spurious event received: {:?}", source), } } else { @@ -79,21 +74,15 @@ impl Subscriber for Block { } } - fn interest_list(&self) -> Vec { + fn init(&mut self, ops: &mut EventOps) { // This function can be called during different points in the device lifetime: // - shortly after device creation, // - on device activation (is-activated already true at this point), // - on device restore from snapshot. if self.is_activated() { - vec![ - EpollEvent::new(EventSet::IN, self.queue_evts[0].as_raw_fd() as u64), - EpollEvent::new(EventSet::IN, self.rate_limiter.as_raw_fd() as u64), - ] + self.register_runtime_events(ops); } else { - vec![EpollEvent::new( - EventSet::IN, - self.activate_evt.as_raw_fd() as u64, - )] + self.register_activate_event(ops); } } } @@ -106,6 +95,7 @@ pub mod tests { use crate::virtio::block::test_utils::{default_block, set_queue}; use crate::virtio::queue::tests::*; use crate::virtio::test_utils::{default_mem, initialize_virtqueue, VirtQueue}; + use event_manager::{EventManager, SubscriberOps}; use virtio_gen::virtio_blk::*; use vm_memory::{Bytes, GuestAddress}; @@ -119,7 +109,7 @@ pub mod tests { initialize_virtqueue(&vq); let block = Arc::new(Mutex::new(block)); - event_manager.add_subscriber(block.clone()).unwrap(); + let _id = event_manager.add_subscriber(block.clone()); let request_type_addr = GuestAddress(vq.dtable[0].addr.get()); let data_addr = GuestAddress(vq.dtable[1].addr.get()); @@ -143,23 +133,6 @@ pub mod tests { let ev_count = event_manager.run_with_timeout(50).unwrap(); assert_eq!(ev_count, 0); - // Manually force a queue event and check it's ignored pre-activation. - { - let mut b = block.lock().unwrap(); - let raw_q_evt = b.queue_evts[0].as_raw_fd() as u64; - // Artificially push event. - b.process( - &EpollEvent::new(EventSet::IN, raw_q_evt), - &mut event_manager, - ); - // Validate there was no queue operation. - assert_eq!( - b.interrupt_evt().read().unwrap_err().kind(), - std::io::ErrorKind::WouldBlock - ); - assert_eq!(vq.used.idx.get(), 0); - } - // Now activate the device. block.lock().unwrap().activate(mem.clone()).unwrap(); // Process the activate event. diff --git a/src/devices/src/virtio/block/test_utils.rs b/src/devices/src/virtio/block/test_utils.rs index 5b1f86fbfea..0894005e73f 100644 --- a/src/devices/src/virtio/block/test_utils.rs +++ b/src/devices/src/virtio/block/test_utils.rs @@ -1,12 +1,8 @@ // Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -use std::os::unix::io::AsRawFd; - use crate::virtio::{Block, CacheType, Queue}; -use polly::event_manager::{EventManager, Subscriber}; use rate_limiter::RateLimiter; -use utils::epoll::{EpollEvent, EventSet}; use utils::tempfile::TempFile; /// Create a default Block instance to be used in tests. @@ -41,10 +37,7 @@ pub fn invoke_handler_for_queue_event(b: &mut Block) { // Trigger the queue event. b.queue_evts[0].write(1).unwrap(); // Handle event. - b.process( - &EpollEvent::new(EventSet::IN, b.queue_evts[0].as_raw_fd() as u64), - &mut EventManager::new().unwrap(), - ); + b.process_queue_event(); // Validate the queue operation finished successfully. assert_eq!(b.interrupt_evt.read().unwrap(), 1); } diff --git a/src/devices/src/virtio/net/event_handler.rs b/src/devices/src/virtio/net/event_handler.rs index be1a1b152b8..2fdf15c5bac 100644 --- a/src/devices/src/virtio/net/event_handler.rs +++ b/src/devices/src/virtio/net/event_handler.rs @@ -3,48 +3,55 @@ use std::os::unix::io::AsRawFd; +use event_manager::{EventOps, Events, MutEventSubscriber}; use logger::{debug, error, warn, IncMetric, METRICS}; -use polly::event_manager::{EventManager, Subscriber}; -use utils::epoll::{EpollEvent, EventSet}; +use utils::epoll::EventSet; use crate::virtio::net::device::Net; use crate::virtio::{VirtioDevice, RX_INDEX, TX_INDEX}; impl Net { - fn process_activate_event(&self, event_manager: &mut EventManager) { + fn register_runtime_events(&self, ops: &mut EventOps) { + if let Err(e) = ops.add(Events::new(&self.queue_evts[RX_INDEX], EventSet::IN)) { + error!("Failed to register rx queue event: {}", e); + } + if let Err(e) = ops.add(Events::new(&self.queue_evts[TX_INDEX], EventSet::IN)) { + error!("Failed to register tx queue event: {}", e); + } + if let Err(e) = ops.add(Events::new(&self.rx_rate_limiter, EventSet::IN)) { + error!("Failed to register rx queue event: {}", e); + } + if let Err(e) = ops.add(Events::new(&self.tx_rate_limiter, EventSet::IN)) { + error!("Failed to register tx queue event: {}", e); + } + if let Err(e) = ops.add(Events::new( + &self.tap, + EventSet::IN | EventSet::EDGE_TRIGGERED, + )) { + error!("Failed to register tap event: {}", e); + } + } + + fn register_activate_event(&self, ops: &mut EventOps) { + if let Err(e) = ops.add(Events::new(&self.activate_evt, EventSet::IN)) { + error!("Failed to register activate event: {}", e); + } + } + + fn process_activate_event(&self, ops: &mut EventOps) { debug!("net: activate event"); if let Err(e) = self.activate_evt.read() { error!("Failed to consume net activate event: {:?}", e); } - let activate_fd = self.activate_evt.as_raw_fd(); - // The subscriber must exist as we previously registered activate_evt via - // `interest_list()`. - let self_subscriber = match event_manager.subscriber(activate_fd) { - Ok(subscriber) => subscriber, - Err(e) => { - error!("Failed to process block activate evt: {:?}", e); - return; - } - }; - - // Interest list changes when the device is activated. - let interest_list = self.interest_list(); - for event in interest_list { - event_manager - .register(event.data() as i32, event, self_subscriber.clone()) - .unwrap_or_else(|e| { - error!("Failed to register net events: {:?}", e); - }); + self.register_runtime_events(ops); + if let Err(e) = ops.remove(Events::new(&self.activate_evt, EventSet::IN)) { + error!("Failed to un-register activate event: {}", e); } - - event_manager.unregister(activate_fd).unwrap_or_else(|e| { - error!("Failed to unregister net activate evt: {:?}", e); - }); } } -impl Subscriber for Net { - fn process(&mut self, event: &EpollEvent, evmgr: &mut EventManager) { +impl MutEventSubscriber for Net { + fn process(&mut self, event: Events, ops: &mut EventOps) { let source = event.fd(); let event_set = event.event_set(); @@ -74,7 +81,7 @@ impl Subscriber for Net { _ if source == virtq_tx_ev_fd => self.process_tx_queue_event(), _ if source == rx_rate_limiter_fd => self.process_rx_rate_limiter_event(), _ if source == tx_rate_limiter_fd => self.process_tx_rate_limiter_event(), - _ if activate_fd == source => self.process_activate_event(evmgr), + _ if activate_fd == source => self.process_activate_event(ops), _ => { warn!("Net: Spurious event received: {:?}", source); METRICS.net.event_fails.inc(); @@ -88,37 +95,24 @@ impl Subscriber for Net { } } - fn interest_list(&self) -> Vec { + fn init(&mut self, ops: &mut EventOps) { // This function can be called during different points in the device lifetime: // - shortly after device creation, // - on device activation (is-activated already true at this point), // - on device restore from snapshot. if self.is_activated() { - vec![ - EpollEvent::new(EventSet::IN, self.queue_evts[RX_INDEX].as_raw_fd() as u64), - EpollEvent::new(EventSet::IN, self.queue_evts[TX_INDEX].as_raw_fd() as u64), - EpollEvent::new(EventSet::IN, self.rx_rate_limiter.as_raw_fd() as u64), - EpollEvent::new(EventSet::IN, self.tx_rate_limiter.as_raw_fd() as u64), - EpollEvent::new( - EventSet::IN | EventSet::EDGE_TRIGGERED, - self.tap.as_raw_fd() as u64, - ), - ] + self.register_runtime_events(ops); } else { - vec![EpollEvent::new( - EventSet::IN, - self.activate_evt.as_raw_fd() as u64, - )] + self.register_activate_event(ops); } } } #[cfg(test)] pub mod tests { - use crate::check_metric_after_block; use crate::virtio::net::test_utils::test::TestHelper; - use crate::virtio::net::test_utils::{NetEvent, NetQueue}; - use logger::{IncMetric, METRICS}; + use crate::virtio::net::test_utils::NetQueue; + use crate::virtio::net::TX_INDEX; #[test] fn test_event_handler() { @@ -133,25 +127,19 @@ pub mod tests { assert_eq!(ev_count, 0); // Manually force a queue event and check it's ignored pre-activation. - th.simulate_event(NetEvent::TxQueue); + th.net().queue_evts[TX_INDEX].write(1).unwrap(); + let ev_count = th.event_manager.run_with_timeout(50).unwrap(); + assert_eq!(ev_count, 0); // Validate there was no queue operation. assert_eq!(th.txq.used.idx.get(), 0); // Now activate the device. th.activate_net(); - // Handle the previously pushed queue event through EventManager. th.event_manager - .run_with_timeout(100) + .run_with_timeout(50) .expect("Metrics event timeout or error."); // Make sure the data queue advanced. assert_eq!(th.txq.used.idx.get(), 1); - - // Inject invalid event. - check_metric_after_block!( - &METRICS.net.event_fails, - 1, - th.simulate_event(NetEvent::Custom(1000)) - ); } } diff --git a/src/devices/src/virtio/net/test_utils.rs b/src/devices/src/virtio/net/test_utils.rs index eed47f10d47..4400aefd956 100644 --- a/src/devices/src/virtio/net/test_utils.rs +++ b/src/devices/src/virtio/net/test_utils.rs @@ -88,7 +88,6 @@ pub enum NetQueue { } pub enum NetEvent { - Custom(i32), RxQueue, RxRateLimiter, Tap, @@ -304,19 +303,19 @@ pub mod test { Net, VirtioDevice, MAX_BUFFER_SIZE, RX_INDEX, TX_INDEX, VIRTQ_DESC_F_NEXT, VIRTQ_DESC_F_WRITE, }; + use event_manager::{EventManager, SubscriberId, SubscriberOps}; use logger::{IncMetric, METRICS}; use net_gen::ETH_HLEN; - use polly::event_manager::{EventManager, Subscriber}; + use vm_memory::{Address, Bytes, GuestAddress, GuestMemoryMmap}; + use std::cmp; use std::mem; use std::os::unix::ffi::OsStrExt; - use std::os::unix::io::AsRawFd; use std::sync::{Arc, Mutex, MutexGuard}; - use utils::epoll::{EpollEvent, EventSet}; - use vm_memory::{Address, Bytes, GuestAddress, GuestMemoryMmap}; pub struct TestHelper<'a> { - pub event_manager: EventManager, + pub event_manager: EventManager>>, + pub subscriber_id: SubscriberId, pub net: Arc>, pub mem: GuestMemoryMmap, pub rxq: VirtQueue<'a>, @@ -342,10 +341,11 @@ pub mod test { assign_queues(&mut net, rxq.create_queue(), txq.create_queue()); let net = Arc::new(Mutex::new(net)); - event_manager.add_subscriber(net.clone()).unwrap(); + let subscriber_id = event_manager.add_subscriber(net.clone()); Self { event_manager, + subscriber_id, net, mem, rxq, @@ -365,18 +365,13 @@ pub mod test { } pub fn simulate_event(&mut self, event: NetEvent) { - let event_fd = match event { - NetEvent::Custom(event_fd) => event_fd, - NetEvent::RxQueue => self.net().queue_evts[RX_INDEX].as_raw_fd(), - NetEvent::RxRateLimiter => self.net().rx_rate_limiter.as_raw_fd(), - NetEvent::Tap => self.net().tap.as_raw_fd(), - NetEvent::TxQueue => self.net().queue_evts[TX_INDEX].as_raw_fd(), - NetEvent::TxRateLimiter => self.net().tx_rate_limiter.as_raw_fd(), + match event { + NetEvent::RxQueue => self.net().process_rx_queue_event(), + NetEvent::RxRateLimiter => self.net().process_rx_rate_limiter_event(), + NetEvent::Tap => self.net().process_tap_rx_event(), + NetEvent::TxQueue => self.net().process_tx_queue_event(), + NetEvent::TxRateLimiter => self.net().process_tx_rate_limiter_event(), }; - self.net.lock().unwrap().process( - &EpollEvent::new(EventSet::IN, event_fd as u64), - &mut self.event_manager, - ); } pub fn data_addr(&self) -> u64 { diff --git a/src/devices/src/virtio/vsock/event_handler.rs b/src/devices/src/virtio/vsock/event_handler.rs index 87ede34accd..993b6aae150 100644 --- a/src/devices/src/virtio/vsock/event_handler.rs +++ b/src/devices/src/virtio/vsock/event_handler.rs @@ -24,9 +24,9 @@ /// - again, attempt to fetch any incoming packets queued by the backend into virtio RX buffers. use std::os::unix::io::AsRawFd; +use event_manager::{EventOps, Events, MutEventSubscriber}; use logger::{debug, error, warn, IncMetric, METRICS}; -use polly::event_manager::{EventManager, Subscriber}; -use utils::epoll::{EpollEvent, EventSet}; +use utils::epoll::EventSet; use super::device::{Vsock, EVQ_INDEX, RXQ_INDEX, TXQ_INDEX}; use super::VsockBackend; @@ -36,12 +36,11 @@ impl Vsock where B: VsockBackend + 'static, { - pub(crate) fn handle_rxq_event(&mut self, event: &EpollEvent) -> bool { + pub(crate) fn handle_rxq_event(&mut self, evset: EventSet) -> bool { debug!("vsock: RX queue event"); - let event_set = event.event_set(); - if event_set != EventSet::IN { - warn!("vsock: rxq unexpected event {:?}", event_set); + if evset != EventSet::IN { + warn!("vsock: rxq unexpected event {:?}", evset); METRICS.vsock.rx_queue_event_fails.inc(); return false; } @@ -57,12 +56,11 @@ where raise_irq } - pub(crate) fn handle_txq_event(&mut self, event: &EpollEvent) -> bool { + pub(crate) fn handle_txq_event(&mut self, evset: EventSet) -> bool { debug!("vsock: TX queue event"); - let event_set = event.event_set(); - if event_set != EventSet::IN { - warn!("vsock: txq unexpected event {:?}", event_set); + if evset != EventSet::IN { + warn!("vsock: txq unexpected event {:?}", evset); METRICS.vsock.tx_queue_event_fails.inc(); return false; } @@ -84,12 +82,11 @@ where raise_irq } - fn handle_evq_event(&mut self, event: &EpollEvent) -> bool { + fn handle_evq_event(&mut self, evset: EventSet) -> bool { debug!("vsock: event queue event"); - let event_set = event.event_set(); - if event_set != EventSet::IN { - warn!("vsock: evq unexpected event {:?}", event_set); + if evset != EventSet::IN { + warn!("vsock: evq unexpected event {:?}", evset); METRICS.vsock.ev_queue_event_fails.inc(); return false; } @@ -101,10 +98,10 @@ where false } - fn notify_backend(&mut self, event: &EpollEvent) -> bool { + fn notify_backend(&mut self, evset: EventSet) -> bool { debug!("vsock: backend event"); - self.backend.notify(event.event_set()); + self.backend.notify(evset); // After the backend has been kicked, it might've freed up some resources, so we // can attempt to send it more data to process. // In particular, if `self.backend.send_pkt()` halted the TX queue processing (by @@ -117,44 +114,46 @@ where raise_irq } - fn handle_activate_event(&self, event_manager: &mut EventManager) { - debug!("vsock: activate event"); - if let Err(e) = self.activate_evt.read() { - error!("Failed to consume vsock activate event: {:?}", e); - } - let activate_fd = self.activate_evt.as_raw_fd(); - // The subscriber must exist as we previously registered activate_evt via - // `interest_list()`. - let self_subscriber = match event_manager.subscriber(activate_fd) { - Ok(subscriber) => subscriber, - Err(e) => { - error!("Failed to process vsock activate evt: {:?}", e); - return; - } - }; + fn register_runtime_events(&self, ops: &mut EventOps) { + if let Err(e) = ops.add(Events::new(&self.queue_events[RXQ_INDEX], EventSet::IN)) { + error!("Failed to register rx queue event: {}", e); + } + if let Err(e) = ops.add(Events::new(&self.queue_events[TXQ_INDEX], EventSet::IN)) { + error!("Failed to register tx queue event: {}", e); + } + if let Err(e) = ops.add(Events::new(&self.queue_events[EVQ_INDEX], EventSet::IN)) { + error!("Failed to register ev queue event: {}", e); + } + if let Err(e) = ops.add(Events::new(&self.backend, self.backend.get_polled_evset())) { + error!("Failed to register vsock backend event: {}", e); + } + } - // Interest list changes when the device is activated. - let interest_list = self.interest_list(); - for event in interest_list { - event_manager - .register(event.data() as i32, event, self_subscriber.clone()) - .unwrap_or_else(|e| { - error!("Failed to register vsock events: {:?}", e); - }); + fn register_activate_event(&self, ops: &mut EventOps) { + if let Err(e) = ops.add(Events::new(&self.activate_evt, EventSet::IN)) { + error!("Failed to register activate event: {}", e); } + } - event_manager.unregister(activate_fd).unwrap_or_else(|e| { - error!("Failed to unregister vsock activate evt: {:?}", e); - }); + fn handle_activate_event(&self, ops: &mut EventOps) { + debug!("vsock: activate event"); + if let Err(e) = self.activate_evt.read() { + error!("Failed to consume net activate event: {:?}", e); + } + self.register_runtime_events(ops); + if let Err(e) = ops.remove(Events::new(&self.activate_evt, EventSet::IN)) { + error!("Failed to un-register activate event: {}", e); + } } } -impl Subscriber for Vsock +impl MutEventSubscriber for Vsock where B: VsockBackend + 'static, { - fn process(&mut self, event: &EpollEvent, event_manager: &mut EventManager) { + fn process(&mut self, event: Events, ops: &mut EventOps) { let source = event.fd(); + let evset = event.event_set(); let rxq = self.queue_events[RXQ_INDEX].as_raw_fd(); let txq = self.queue_events[TXQ_INDEX].as_raw_fd(); let evq = self.queue_events[EVQ_INDEX].as_raw_fd(); @@ -164,14 +163,14 @@ where if self.is_activated() { let mut raise_irq = false; match source { - _ if source == rxq => raise_irq = self.handle_rxq_event(event), - _ if source == txq => raise_irq = self.handle_txq_event(event), - _ if source == evq => raise_irq = self.handle_evq_event(event), + _ if source == rxq => raise_irq = self.handle_rxq_event(evset), + _ if source == txq => raise_irq = self.handle_txq_event(evset), + _ if source == evq => raise_irq = self.handle_evq_event(evset), _ if source == backend => { - raise_irq = self.notify_backend(event); + raise_irq = self.notify_backend(evset); } _ if source == activate_evt => { - self.handle_activate_event(event_manager); + self.handle_activate_event(ops); } _ => warn!("Unexpected vsock event received: {:?}", source), } @@ -186,35 +185,15 @@ where } } - fn interest_list(&self) -> Vec { + fn init(&mut self, ops: &mut EventOps) { // This function can be called during different points in the device lifetime: // - shortly after device creation, // - on device activation (is-activated already true at this point), // - on device restore from snapshot. if self.is_activated() { - vec![ - EpollEvent::new( - EventSet::IN, - self.queue_events[RXQ_INDEX].as_raw_fd() as u64, - ), - EpollEvent::new( - EventSet::IN, - self.queue_events[TXQ_INDEX].as_raw_fd() as u64, - ), - EpollEvent::new( - EventSet::IN, - self.queue_events[EVQ_INDEX].as_raw_fd() as u64, - ), - EpollEvent::new( - self.backend.get_polled_evset(), - self.backend.as_raw_fd() as u64, - ), - ] + self.register_runtime_events(ops); } else { - vec![EpollEvent::new( - EventSet::IN, - self.activate_evt.as_raw_fd() as u64, - )] + self.register_activate_event(ops); } } } @@ -231,6 +210,7 @@ mod tests { use crate::virtio::vsock::test_utils::{EventHandlerContext, TestContext}; use crate::virtio::VIRTIO_MMIO_INT_VRING; use crate::Error as DeviceError; + use event_manager::{EventManager, SubscriberOps}; use vm_memory::Bytes; #[test] @@ -337,9 +317,7 @@ mod tests { let mut ctx = test_ctx.create_event_handler_context(); ctx.mock_activate(test_ctx.mem.clone()); - assert!(!ctx - .device - .handle_txq_event(&EpollEvent::new(EventSet::IN, 0))); + assert!(!ctx.device.handle_txq_event(EventSet::IN)); } } @@ -399,9 +377,7 @@ mod tests { let mut ctx = test_ctx.create_event_handler_context(); ctx.mock_activate(test_ctx.mem.clone()); ctx.device.backend.set_pending_rx(false); - assert!(!ctx - .device - .handle_rxq_event(&EpollEvent::new(EventSet::IN, 0))); + assert!(!ctx.device.handle_rxq_event(EventSet::IN)); } } @@ -412,9 +388,7 @@ mod tests { let test_ctx = TestContext::new(); let mut ctx = test_ctx.create_event_handler_context(); ctx.device.backend.set_pending_rx(false); - assert!(!ctx - .device - .handle_evq_event(&EpollEvent::new(EventSet::IN, 0))); + assert!(!ctx.device.handle_evq_event(EventSet::IN)); } } @@ -429,7 +403,7 @@ mod tests { ctx.mock_activate(test_ctx.mem.clone()); ctx.device.backend.set_pending_rx(true); - ctx.device.notify_backend(&EpollEvent::new(EventSet::IN, 0)); + ctx.device.notify_backend(EventSet::IN); // The backend should've received this event. assert_eq!(ctx.device.backend.evset, Some(EventSet::IN)); @@ -448,7 +422,7 @@ mod tests { ctx.mock_activate(test_ctx.mem.clone()); ctx.device.backend.set_pending_rx(false); - ctx.device.notify_backend(&EpollEvent::new(EventSet::IN, 0)); + ctx.device.notify_backend(EventSet::IN); // The backend should've received this event. assert_eq!(ctx.device.backend.evset, Some(EventSet::IN)); @@ -573,7 +547,7 @@ mod tests { } = test_ctx.create_event_handler_context(); let vsock = Arc::new(Mutex::new(device)); - event_manager.add_subscriber(vsock.clone()).unwrap(); + let _id = event_manager.add_subscriber(vsock.clone()); // Push a queue event // - the driver has something to send (there's data in the TX queue); and @@ -591,14 +565,12 @@ mod tests { // Manually force a queue event and check it's ignored pre-activation. { - let mut device = vsock.lock().unwrap(); + let device = vsock.lock().unwrap(); - let raw_txq_evt = device.queue_events[TXQ_INDEX].as_raw_fd() as u64; // Artificially push event. - device.process( - &EpollEvent::new(EventSet::IN, raw_txq_evt), - &mut event_manager, - ); + device.queue_events[TXQ_INDEX].write(1).unwrap(); + let ev_count = event_manager.run_with_timeout(50).unwrap(); + assert_eq!(ev_count, 0); // Both available RX and TX descriptors should be untouched. assert_eq!(guest_rxvq.used.idx.get(), 0); diff --git a/src/devices/src/virtio/vsock/test_utils.rs b/src/devices/src/virtio/vsock/test_utils.rs index 5ea8adb5c0a..530223bc858 100644 --- a/src/devices/src/virtio/vsock/test_utils.rs +++ b/src/devices/src/virtio/vsock/test_utils.rs @@ -10,7 +10,7 @@ use crate::virtio::{ VirtioDevice, Vsock, VsockBackend, VsockChannel, VsockEpollListener, VsockError, VIRTQ_DESC_F_NEXT, VIRTQ_DESC_F_WRITE, }; -use utils::epoll::{EpollEvent, EventSet}; +use utils::epoll::EventSet; use utils::eventfd::EventFd; use vm_memory::{GuestAddress, GuestMemoryMmap}; @@ -186,13 +186,11 @@ impl<'a> EventHandlerContext<'a> { pub fn signal_txq_event(&mut self) { self.device.queue_events[TXQ_INDEX].write(1).unwrap(); - self.device - .handle_txq_event(&EpollEvent::new(EventSet::IN, 0)); + self.device.handle_txq_event(EventSet::IN); } pub fn signal_rxq_event(&mut self) { self.device.queue_events[RXQ_INDEX].write(1).unwrap(); - self.device - .handle_rxq_event(&EpollEvent::new(EventSet::IN, 0)); + self.device.handle_rxq_event(EventSet::IN); } } diff --git a/src/devices/tests/integration_tests.rs b/src/devices/tests/integration_tests.rs index 8852f8b8df6..dc4ea92b543 100644 --- a/src/devices/tests/integration_tests.rs +++ b/src/devices/tests/integration_tests.rs @@ -7,9 +7,10 @@ use std::io; use std::os::raw::{c_int, c_void}; use std::sync::{Arc, Mutex}; +use event_manager::{EventManager, SubscriberOps}; + use devices::legacy::Serial; use devices::BusDevice; -use polly::event_manager::EventManager; use serial_utils::MockSerialInput; use utils::eventfd::EventFd; @@ -47,7 +48,7 @@ fn test_issue_serial_hangup_anon_pipe_while_registered_stdin() { // Register the reading end of the pipe to the event manager, to be processed later on. let mut event_manager = EventManager::new().unwrap(); - event_manager.add_subscriber(serial.clone()).unwrap(); + let _id = event_manager.add_subscriber(serial.clone()); // `EventSet::IN` was received on stdin. The event handling will consume // 64 bytes from stdin. The stdin monitoring is still armed. @@ -165,7 +166,7 @@ fn test_issue_hangup() { // Register the reading end of the pipe to the event manager, to be processed later on. let mut event_manager = EventManager::new().unwrap(); - event_manager.add_subscriber(serial.clone()).unwrap(); + let _id = event_manager.add_subscriber(serial.clone()); let mut ev_count = event_manager.run().unwrap(); assert_eq!(ev_count, 1); @@ -209,7 +210,7 @@ fn test_issue_serial_hangup_anon_pipe_while_unregistered_stdin() { // Register the reading end of the pipe to the event manager, to be processed later on. let mut event_manager = EventManager::new().unwrap(); - event_manager.add_subscriber(serial.clone()).unwrap(); + let _id = event_manager.add_subscriber(serial.clone()); // `EventSet::IN` was received on stdin. The event handling will consume // 64 bytes from stdin. The stdin monitoring is still armed. diff --git a/src/firecracker/Cargo.toml b/src/firecracker/Cargo.toml index 0604b4de2e4..f970a2ad6c6 100644 --- a/src/firecracker/Cargo.toml +++ b/src/firecracker/Cargo.toml @@ -9,13 +9,13 @@ homepage = "https://firecracker-microvm.github.io/" license = "Apache-2.0" [dependencies] +event-manager = ">=0.2.1" libc = ">=0.2.39" timerfd = ">=1.0" api_server = { path = "../api_server" } logger = { path = "../logger" } mmds = { path = "../mmds" } -polly = { path = "../polly" } seccompiler = { path = "../seccompiler" } snapshot = { path = "../snapshot"} utils = { path = "../utils" } diff --git a/src/firecracker/src/api_server_adapter.rs b/src/firecracker/src/api_server_adapter.rs index 87dcf7754a5..619a162ef4d 100644 --- a/src/firecracker/src/api_server_adapter.rs +++ b/src/firecracker/src/api_server_adapter.rs @@ -10,19 +10,16 @@ use std::sync::{Arc, Mutex}; use std::thread; use api_server::{ApiRequest, ApiResponse, ApiServer}; +use event_manager::{EventOps, Events, MutEventSubscriber, SubscriberOps}; use logger::{error, warn, ProcessTimeReporter}; use mmds::MMDS; -use polly::event_manager::{EventManager, Subscriber}; use seccompiler::BpfThreadMap; -use utils::{ - epoll::{EpollEvent, EventSet}, - eventfd::EventFd, -}; +use utils::{epoll::EventSet, eventfd::EventFd}; use vmm::{ resources::VmResources, rpc_interface::{PrebootApiController, RuntimeApiController, VmmAction}, vmm_config::instance_info::InstanceInfo, - ExitCode, Vmm, + EventManager, ExitCode, Vmm, }; struct ApiServerAdapter { @@ -49,10 +46,7 @@ impl ApiServerAdapter { to_api, controller: RuntimeApiController::new(vm_resources, vmm.clone()), })); - event_manager - .add_subscriber(api_adapter) - .expect("Cannot register the api event to the event manager."); - + event_manager.add_subscriber(api_adapter); loop { event_manager .run() @@ -72,9 +66,9 @@ impl ApiServerAdapter { .expect("one-shot channel closed"); } } -impl Subscriber for ApiServerAdapter { +impl MutEventSubscriber for ApiServerAdapter { /// Handle a read event (EPOLLIN). - fn process(&mut self, event: &EpollEvent, _: &mut EventManager) { + fn process(&mut self, event: Events, _: &mut EventOps) { let source = event.fd(); let event_set = event.event_set(); @@ -115,11 +109,10 @@ impl Subscriber for ApiServerAdapter { } } - fn interest_list(&self) -> Vec { - vec![EpollEvent::new( - EventSet::IN, - self.api_event_fd.as_raw_fd() as u64, - )] + fn init(&mut self, ops: &mut EventOps) { + if let Err(e) = ops.add(Events::new(&self.api_event_fd, EventSet::IN)) { + error!("Failed to register activate event: {}", e); + } } } @@ -178,9 +171,7 @@ pub(crate) fn run_with_api( let mut event_manager = EventManager::new().expect("Unable to create EventManager"); // Create the firecracker metrics object responsible for periodically printing metrics. let firecracker_metrics = Arc::new(Mutex::new(super::metrics::PeriodicMetrics::new())); - event_manager - .add_subscriber(firecracker_metrics.clone()) - .expect("Cannot register the metrics event to the event manager."); + event_manager.add_subscriber(firecracker_metrics.clone()); // Configure, build and start the microVM. let build_result = match config_json { diff --git a/src/firecracker/src/main.rs b/src/firecracker/src/main.rs index 1aa7d1d7109..865005ecae7 100644 --- a/src/firecracker/src/main.rs +++ b/src/firecracker/src/main.rs @@ -10,20 +10,19 @@ use std::path::PathBuf; use std::process; use std::sync::{Arc, Mutex}; +use event_manager::SubscriberOps; use logger::{error, info, warn, IncMetric, ProcessTimeReporter, LOGGER, METRICS}; -use polly::event_manager::EventManager; use seccompiler::BpfThreadMap; use snapshot::Snapshot; use utils::arg_parser::{ArgParser, Argument, Arguments}; use utils::terminal::Terminal; use utils::validators::validate_instance_id; -use vmm::resources::VmResources; use vmm::seccomp_filters::{get_filters, SeccompConfig}; use vmm::signal_handler::register_signal_handlers; use vmm::version_map::{FC_VERSION_TO_SNAP_VERSION, VERSION_MAP}; use vmm::vmm_config::instance_info::{InstanceInfo, VmState}; use vmm::vmm_config::logger::{init_logger, LoggerConfig, LoggerLevel}; -use vmm::ExitCode; +use vmm::{resources::VmResources, EventManager, ExitCode}; // The reason we place default API socket under /run is that API socket is a // runtime file. @@ -464,9 +463,7 @@ fn run_without_api( // Create the firecracker metrics object responsible for periodically printing metrics. let firecracker_metrics = Arc::new(Mutex::new(metrics::PeriodicMetrics::new())); - event_manager - .add_subscriber(firecracker_metrics.clone()) - .expect("Cannot register the metrics event to the event manager."); + event_manager.add_subscriber(firecracker_metrics.clone()); // Build the microVm. We can ignore VmResources since it's not used without api. let (_, vmm) = match build_microvm_from_json( diff --git a/src/firecracker/src/metrics.rs b/src/firecracker/src/metrics.rs index 998ec8a8ef5..ddca21a6d0b 100644 --- a/src/firecracker/src/metrics.rs +++ b/src/firecracker/src/metrics.rs @@ -4,10 +4,10 @@ use std::os::unix::io::AsRawFd; use std::time::Duration; +use event_manager::{EventOps, Events, MutEventSubscriber}; use logger::{error, warn, IncMetric, METRICS}; -use polly::event_manager::{EventManager, Subscriber}; use timerfd::{ClockId, SetTimeFlags, TimerFd, TimerState}; -use utils::epoll::{EpollEvent, EventSet}; +use utils::epoll::EventSet; /// Metrics reporting period. pub(crate) const WRITE_METRICS_PERIOD_MS: u64 = 60000; @@ -60,9 +60,9 @@ impl PeriodicMetrics { } } -impl Subscriber for PeriodicMetrics { +impl MutEventSubscriber for PeriodicMetrics { /// Handle a read event (EPOLLIN). - fn process(&mut self, event: &EpollEvent, _: &mut EventManager) { + fn process(&mut self, event: Events, _: &mut EventOps) { let source = event.fd(); let event_set = event.event_set(); @@ -85,11 +85,10 @@ impl Subscriber for PeriodicMetrics { } } - fn interest_list(&self) -> Vec { - vec![EpollEvent::new( - EventSet::IN, - self.write_metrics_event_fd.as_raw_fd() as u64, - )] + fn init(&mut self, ops: &mut EventOps) { + if let Err(e) = ops.add(Events::new(&self.write_metrics_event_fd, EventSet::IN)) { + error!("Failed to register metrics event: {}", e); + } } } @@ -98,48 +97,13 @@ pub mod tests { use std::sync::{Arc, Mutex}; use super::*; - use polly::event_manager::EventManager; - use utils::eventfd::EventFd; - - #[test] - fn test_interest_list() { - let metrics = PeriodicMetrics::new(); - let interest_list = metrics.interest_list(); - assert_eq!(interest_list.len(), 1); - assert_eq!( - interest_list[0].data() as i32, - metrics.write_metrics_event_fd.as_raw_fd() - ); - assert_eq!( - EventSet::from_bits(interest_list[0].events()).unwrap(), - EventSet::IN - ); - } + use event_manager::{EventManager, SubscriberOps}; #[test] fn test_periodic_metrics() { let mut event_manager = EventManager::new().expect("Unable to create EventManager"); - let mut metrics = PeriodicMetrics::new(); - - // Test invalid read event. - let unrelated_object = EventFd::new(libc::EFD_NONBLOCK).unwrap(); - let unrelated_event = EpollEvent::new(EventSet::IN, unrelated_object.as_raw_fd() as u64); - metrics.process(&unrelated_event, &mut event_manager); - // No flush happened. - assert_eq!(metrics.flush_counter, 0); - - // Test unsupported event type. - let unsupported_event = EpollEvent::new( - EventSet::OUT, - metrics.write_metrics_event_fd.as_raw_fd() as u64, - ); - metrics.process(&unsupported_event, &mut event_manager); - assert_eq!(metrics.flush_counter, 0); - - let metrics = Arc::new(Mutex::new(metrics)); - event_manager - .add_subscriber(metrics.clone()) - .expect("Cannot register the metrics event to the event manager."); + let metrics = Arc::new(Mutex::new(PeriodicMetrics::new())); + event_manager.add_subscriber(metrics.clone()); let flush_period_ms = 50; metrics diff --git a/src/polly/Cargo.toml b/src/polly/Cargo.toml deleted file mode 100644 index d1f9ab6220f..00000000000 --- a/src/polly/Cargo.toml +++ /dev/null @@ -1,10 +0,0 @@ -[package] -name = "polly" -version = "0.0.1" -authors = ["Amazon Firecracker team "] -edition = "2018" - -[dependencies] -libc = ">=0.2.39" - -utils = { path="../utils" } diff --git a/src/polly/src/event_manager.rs b/src/polly/src/event_manager.rs deleted file mode 100644 index c2203e4e565..00000000000 --- a/src/polly/src/event_manager.rs +++ /dev/null @@ -1,497 +0,0 @@ -// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -use std::collections::HashMap; -use std::fmt::Formatter; -use std::io; -use std::os::unix::io::{AsRawFd, RawFd}; -use std::sync::{Arc, Mutex}; - -use utils::epoll::{self, Epoll, EpollEvent}; - -pub type Result = std::result::Result; -pub type Pollable = RawFd; - -/// Errors associated with epoll events handling. -pub enum Error { - /// Cannot create epoll fd. - EpollCreate(io::Error), - /// Polling I/O error. - Poll(io::Error), - /// The specified pollable already registered. - AlreadyExists(Pollable), - /// The specified pollable is not registered. - NotFound(Pollable), -} - -impl std::fmt::Debug for Error { - fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { - use self::Error::*; - - match self { - EpollCreate(err) => write!(f, "Unable to create epoll fd: {}", err), - Poll(err) => write!(f, "Error during epoll call: {}", err), - AlreadyExists(pollable) => write!( - f, - "A handler for the specified pollable {} already exists.", - pollable - ), - NotFound(pollable) => write!( - f, - "A handler for the specified pollable {} was not found.", - pollable - ), - } - } -} - -/// A trait to express the ability to respond to I/O event readiness -/// using callbacks. -pub trait Subscriber { - /// Callback called when an event is available. - /// - /// # Arguments - /// * event - the available `EpollEvent` ready for processing - /// * event_manager - Reference to the `EventManager` that gives the implementor - /// the possibility to directly call the required update operations. - /// The only functions safe to call on this `EventManager` reference - /// are `register`, `unregister` and `modify` which correspond to - /// the `libc::epoll_ctl` operations. - fn process(&mut self, event: &EpollEvent, event_manager: &mut EventManager); - - /// Returns a list of `EpollEvent` that this subscriber is interested in. - fn interest_list(&self) -> Vec; -} - -/// Manages I/O notifications using epoll mechanism. -pub struct EventManager { - epoll: Epoll, - subscribers: HashMap>>, - ready_events: Vec, -} - -impl AsRawFd for EventManager { - fn as_raw_fd(&self) -> RawFd { - self.epoll.as_raw_fd() - } -} - -impl EventManager { - const EVENT_BUFFER_SIZE: usize = 128; - - /// Create a new EventManager. - pub fn new() -> Result { - let epoll_fd = epoll::Epoll::new().map_err(Error::EpollCreate)?; - - Ok(EventManager { - epoll: epoll_fd, - subscribers: HashMap::new(), - // This buffer is used for storing the events returned by `epoll_wait()`. - // We preallocate memory for this buffer in order to not repeat this - // operation every time `run()` loop is executed. - ready_events: vec![epoll::EpollEvent::default(); EventManager::EVENT_BUFFER_SIZE], - }) - } - - /// Returns a clone of the subscriber associated with the `fd`. - pub fn subscriber(&self, fd: Pollable) -> Result>> { - self.subscribers - .get(&fd) - .ok_or(Error::NotFound(fd)) - .map(|subscriber| subscriber.clone()) - } - - /// Register a new subscriber. All events that the subscriber is interested are registered. - /// - // TODO: Remove this workaround method. The desired state in the future is for each - // subscriber to call `register` directly when it needs to register an event and not have - // all events registered at once. This way we can also remove the `interest_list` which is - // only used once in this function. - pub fn add_subscriber(&mut self, subscriber: Arc>) -> Result<()> { - // Expecting here is safe because we want to panic in case the lock is poisoned. - let interest_list = subscriber.lock().expect("Poisoned lock").interest_list(); - - for event in interest_list { - self.register(event.data() as i32, event, subscriber.clone())? - } - - Ok(()) - } - - /// Register a new `pollable` file descriptor with the corresponding `epoll_event` - /// for `subscriber`. - pub fn register( - &mut self, - pollable: Pollable, - epoll_event: EpollEvent, - subscriber: Arc>, - ) -> Result<()> { - if self.subscribers.contains_key(&pollable) { - return Err(Error::AlreadyExists(pollable)); - }; - - self.epoll - .ctl(epoll::ControlOperation::Add, pollable, epoll_event) - .map_err(Error::Poll)?; - - self.subscribers.insert(pollable, subscriber); - Ok(()) - } - - /// Unregister the `pollable` file descriptor. - pub fn unregister(&mut self, pollable: Pollable) -> Result<()> { - match self.subscribers.remove(&pollable) { - Some(_) => { - self.epoll - .ctl( - epoll::ControlOperation::Delete, - pollable, - epoll::EpollEvent::default(), - ) - .map_err(Error::Poll)?; - } - None => { - return Err(Error::NotFound(pollable)); - } - } - Ok(()) - } - - /// Update the events monitored by `pollable`. - pub fn modify(&mut self, pollable: Pollable, epoll_event: EpollEvent) -> Result<()> { - if self.subscribers.contains_key(&pollable) { - self.epoll - .ctl(epoll::ControlOperation::Modify, pollable, epoll_event) - .map_err(Error::Poll)?; - } else { - return Err(Error::NotFound(pollable)); - } - - Ok(()) - } - - /// Wait for events, then dispatch to the registered event handlers. - pub fn run(&mut self) -> Result { - self.run_with_timeout(-1) - } - - /// Wait for events for a maximum timeout of `miliseconds`. Dispatch the events to the - /// registered signal handlers. - pub fn run_with_timeout(&mut self, milliseconds: i32) -> Result { - let event_count = match self.epoll.wait(milliseconds, &mut self.ready_events[..]) { - Ok(event_count) => event_count, - Err(e) if e.raw_os_error() == Some(libc::EINTR) => 0, - Err(e) => return Err(Error::Poll(e)), - }; - self.dispatch_events(event_count); - - Ok(event_count) - } - - fn dispatch_events(&mut self, event_count: usize) { - // Use the temporary, pre-allocated buffer to check ready events. - for ev_index in 0..event_count { - let event = &self.ready_events[ev_index].clone(); - let pollable = event.fd(); - - if self.subscribers.contains_key(&pollable) { - self.subscribers - .get_mut(&pollable) - .unwrap() // Safe because we have already checked existence - .clone() - .lock() - .expect("Poisoned lock") - .process(&event, self); - } - // TODO: Should we log an error in case the subscriber does not exist? - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use utils::epoll::EventSet; - use utils::eventfd::EventFd; - - struct DummySubscriber { - event_fd_1: EventFd, - event_fd_2: EventFd, - - // Flags used for checking that the event manager called the `process` - // function for ev1/ev2. - processed_ev1_out: bool, - processed_ev2_out: bool, - processed_ev1_in: bool, - - // Flags used for driving register/unregister/modify of events from - // outside of the `process` function. - register_ev2: bool, - unregister_ev1: bool, - modify_ev1: bool, - } - - impl DummySubscriber { - fn new() -> Self { - DummySubscriber { - event_fd_1: EventFd::new(0).unwrap(), - event_fd_2: EventFd::new(0).unwrap(), - processed_ev1_out: false, - processed_ev2_out: false, - processed_ev1_in: false, - register_ev2: false, - unregister_ev1: false, - modify_ev1: false, - } - } - } - - impl DummySubscriber { - fn register_ev2(&mut self) { - self.register_ev2 = true; - } - - fn unregister_ev1(&mut self) { - self.unregister_ev1 = true; - } - - fn modify_ev1(&mut self) { - self.modify_ev1 = true; - } - - fn processed_ev1_out(&self) -> bool { - self.processed_ev1_out - } - - fn processed_ev2_out(&self) -> bool { - self.processed_ev2_out - } - - fn processed_ev1_in(&self) -> bool { - self.processed_ev1_in - } - - fn reset_state(&mut self) { - self.processed_ev1_out = false; - self.processed_ev2_out = false; - self.processed_ev1_in = false; - } - - fn handle_updates(&mut self, event_manager: &mut EventManager) { - if self.register_ev2 { - event_manager - .register( - self.event_fd_2.as_raw_fd(), - EpollEvent::new(EventSet::OUT, self.event_fd_2.as_raw_fd() as u64), - event_manager - .subscriber(self.event_fd_1.as_raw_fd()) - .unwrap(), - ) - .unwrap(); - self.register_ev2 = false; - } - - if self.unregister_ev1 { - event_manager - .unregister(self.event_fd_1.as_raw_fd()) - .unwrap(); - self.unregister_ev1 = false; - } - - if self.modify_ev1 { - event_manager - .modify( - self.event_fd_1.as_raw_fd(), - EpollEvent::new(EventSet::IN, self.event_fd_1.as_raw_fd() as u64), - ) - .unwrap(); - self.modify_ev1 = false; - } - } - - fn handle_in(&mut self, source: RawFd) { - if self.event_fd_1.as_raw_fd() == source { - self.processed_ev1_in = true; - } - } - - fn handle_out(&mut self, source: RawFd) { - match source { - _ if self.event_fd_1.as_raw_fd() == source => { - self.processed_ev1_out = true; - } - _ if self.event_fd_2.as_raw_fd() == source => { - self.processed_ev2_out = true; - } - _ => {} - } - } - } - - impl Subscriber for DummySubscriber { - fn process(&mut self, event: &EpollEvent, event_manager: &mut EventManager) { - let source = event.data() as i32; - let event_set = EventSet::from_bits(event.events()).unwrap(); - - // We only know how to treat EPOLLOUT and EPOLLIN. - // If we received anything else just stop processing the event. - let all_but_in_out = EventSet::all() - EventSet::OUT - EventSet::IN; - if event_set.intersects(all_but_in_out) { - return; - } - - self.handle_updates(event_manager); - - match event_set { - EventSet::IN => self.handle_in(source), - EventSet::OUT => self.handle_out(source), - _ => {} - } - } - - fn interest_list(&self) -> Vec { - vec![EpollEvent::new( - EventSet::OUT, - self.event_fd_1.as_raw_fd() as u64, - )] - } - } - - // Test that registering a new event while processing an existing event works. - #[test] - fn test_register() { - let mut event_manager = EventManager::new().unwrap(); - let dummy_subscriber = Arc::new(Mutex::new(DummySubscriber::new())); - - event_manager - .add_subscriber(dummy_subscriber.clone()) - .unwrap(); - - dummy_subscriber.lock().unwrap().register_ev2(); - - // When running the loop the first time, ev1 should be processed, but ev2 shouldn't - // because it was just added as part of processing ev1. - event_manager.run().unwrap(); - assert_eq!(dummy_subscriber.lock().unwrap().processed_ev1_out(), true); - assert_eq!(dummy_subscriber.lock().unwrap().processed_ev2_out(), false); - - // Check that both ev1 and ev2 are processed. - dummy_subscriber.lock().unwrap().reset_state(); - event_manager.run().unwrap(); - assert_eq!(dummy_subscriber.lock().unwrap().processed_ev1_out(), true); - assert_eq!(dummy_subscriber.lock().unwrap().processed_ev2_out(), true); - } - - // Test that unregistering an event while processing another one works. - #[test] - fn test_unregister() { - let mut event_manager = EventManager::new().unwrap(); - let dummy_subscriber = Arc::new(Mutex::new(DummySubscriber::new())); - - event_manager - .add_subscriber(dummy_subscriber.clone()) - .unwrap(); - - // Disable ev1. We should only receive this event once. - dummy_subscriber.lock().unwrap().unregister_ev1(); - - event_manager.run().unwrap(); - assert_eq!(dummy_subscriber.lock().unwrap().processed_ev1_out(), true); - - dummy_subscriber.lock().unwrap().reset_state(); - - // We expect no events to be available. Let's run with timeout so that run exists. - event_manager.run_with_timeout(100).unwrap(); - assert_eq!(dummy_subscriber.lock().unwrap().processed_ev1_out(), false); - } - - #[test] - fn test_modify() { - let mut event_manager = EventManager::new().unwrap(); - let dummy_subscriber = Arc::new(Mutex::new(DummySubscriber::new())); - - event_manager - .add_subscriber(dummy_subscriber.clone()) - .unwrap(); - - // Modify ev1 so that it waits for EPOLL_IN. - dummy_subscriber.lock().unwrap().modify_ev1(); - event_manager.run().unwrap(); - assert_eq!(dummy_subscriber.lock().unwrap().processed_ev1_out(), true); - assert_eq!(dummy_subscriber.lock().unwrap().processed_ev2_out(), false); - - dummy_subscriber.lock().unwrap().reset_state(); - - // Make sure ev1 is ready for IN so that we don't loop forever. - dummy_subscriber - .lock() - .unwrap() - .event_fd_1 - .write(1) - .unwrap(); - - event_manager.run().unwrap(); - assert_eq!(dummy_subscriber.lock().unwrap().processed_ev1_out(), false); - assert_eq!(dummy_subscriber.lock().unwrap().processed_ev2_out(), false); - assert_eq!(dummy_subscriber.lock().unwrap().processed_ev1_in(), true); - - // Create a valid epoll event, but do not register it to check error path for modify. - let event_fd = EventFd::new(0).unwrap(); - let event = EpollEvent::new(EventSet::IN, event_fd.as_raw_fd() as u64); - let result = event_manager.modify(event_fd.as_raw_fd(), event); - match result { - Err(Error::NotFound(_)) => {} - _ => panic!("Modifying event did not fail with expected error."), - }; - } - - // Test that registering the same event twice throws an error. - #[test] - fn test_register_errors() { - let mut event_manager = EventManager::new().unwrap(); - let dummy_subscriber = Arc::new(Mutex::new(DummySubscriber::new())); - - event_manager - .add_subscriber(dummy_subscriber.clone()) - .unwrap(); - - assert!(event_manager.add_subscriber(dummy_subscriber).is_err()) - } - - #[test] - fn test_unregister_errors() { - let mut event_manager = EventManager::new().unwrap(); - let dummy_subscriber = Arc::new(Mutex::new(DummySubscriber::new())); - - event_manager - .add_subscriber(dummy_subscriber.clone()) - .unwrap(); - - // At this point ev2 is not registered. Check that unregistering it throws an error. - assert!(event_manager - .unregister(dummy_subscriber.lock().unwrap().event_fd_2.as_raw_fd()) - .is_err()); - - // Try to unregister ev1 twice. Only the first call should be successful. - assert!(event_manager - .unregister(dummy_subscriber.lock().unwrap().event_fd_1.as_raw_fd()) - .is_ok()); - assert!(event_manager - .unregister(dummy_subscriber.lock().unwrap().event_fd_1.as_raw_fd()) - .is_err()); - } - - #[test] - fn test_get_handler() { - let mut event_manager = EventManager::new().unwrap(); - let dummy_subscriber = Arc::new(Mutex::new(DummySubscriber::new())); - - event_manager - .add_subscriber(dummy_subscriber.clone()) - .unwrap(); - - let dummy_fd = dummy_subscriber.lock().unwrap().event_fd_1.as_raw_fd(); - assert!(event_manager.subscriber(dummy_fd).is_ok()); - assert!(event_manager.subscriber(-1).is_err()); - } -} diff --git a/src/polly/src/lib.rs b/src/polly/src/lib.rs deleted file mode 100644 index 416f1b570d5..00000000000 --- a/src/polly/src/lib.rs +++ /dev/null @@ -1,3 +0,0 @@ -// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -pub mod event_manager; diff --git a/src/vmm/Cargo.toml b/src/vmm/Cargo.toml index df87486febf..44ae501e765 100644 --- a/src/vmm/Cargo.toml +++ b/src/vmm/Cargo.toml @@ -5,12 +5,14 @@ authors = ["Amazon Firecracker team "] edition = "2018" [dependencies] +event-manager = ">=0.2.1" lazy_static = ">=1.4.0" libc = ">=0.2.39" serde = { version = ">=1.0.27", features = ["derive"] } serde_json = ">=1.0.9" versionize = ">=0.1.6" versionize_derive = ">=0.1.3" + vm-memory = { path = "../vm-memory" } arch = { path = "../arch" } devices = { path = "../devices" } @@ -19,7 +21,6 @@ kvm-bindings = { version = ">=0.4.0", features = ["fam-wrappers"] } kvm-ioctls = ">=0.8.0" logger = { path = "../logger" } mmds = { path = "../mmds" } -polly = { path = "../polly" } rate_limiter = { path = "../rate_limiter" } seccompiler = { path = "../seccompiler" } snapshot = { path = "../snapshot"} diff --git a/src/vmm/src/builder.rs b/src/vmm/src/builder.rs index 27ccfbd0f69..b4bc962022b 100644 --- a/src/vmm/src/builder.rs +++ b/src/vmm/src/builder.rs @@ -22,15 +22,15 @@ use crate::vstate::{ vcpu::{Vcpu, VcpuConfig}, vm::Vm, }; -use crate::{device_manager, Error, Vmm, VmmEventsObserver}; +use crate::{device_manager, Error, EventManager, Vmm, VmmEventsObserver}; use crate::vmm_config::instance_info::InstanceInfo; use arch::InitrdConfig; use devices::legacy::Serial; use devices::virtio::{Balloon, Block, MmioTransport, Net, VirtioDevice, Vsock, VsockUnixBackend}; +use event_manager::{MutEventSubscriber, SubscriberOps}; use kernel::cmdline::Cmdline as KernelCmdline; use logger::{error, warn}; -use polly::event_manager::{Error as EventManagerError, EventManager, Subscriber}; use seccompiler::BpfThreadMap; use snapshot::Persist; use utils::eventfd::EventFd; @@ -73,8 +73,6 @@ pub enum StartMicrovmError { NetDeviceNotConfigured, /// Cannot open the block device backing file. OpenBlockDevice(io::Error), - /// Cannot register an EventHandler. - RegisterEvent(EventManagerError), /// Cannot initialize a MMIO Device or add a device to the MMIO Bus or cmdline. RegisterMmioDevice(device_manager::mmio::Error), /// Cannot restore microvm state. @@ -150,7 +148,6 @@ impl Display for StartMicrovmError { write!(f, "Cannot open the block device backing file. {}", err_msg) } - RegisterEvent(err) => write!(f, "Cannot register EventHandler. {:?}", err), RegisterMmioDevice(err) => { let mut err_msg = format!("{}", err); err_msg = err_msg.replace("\"", ""); @@ -393,9 +390,7 @@ pub fn build_microvm_for_boot( vmm.resume_vm().map_err(Internal)?; let vmm = Arc::new(Mutex::new(vmm)); - event_manager - .add_subscriber(vmm.clone()) - .map_err(RegisterEvent)?; + event_manager.add_subscriber(vmm.clone()); Ok(vmm) } @@ -469,9 +464,7 @@ pub fn build_microvm_from_snapshot( .map_err(RestoreMicrovmState)?; let vmm = Arc::new(Mutex::new(vmm)); - event_manager - .add_subscriber(vmm.clone()) - .map_err(StartMicrovmError::RegisterEvent)?; + event_manager.add_subscriber(vmm.clone()); // Load seccomp filters for the VMM thread. // Keep this as the last step of the building process. @@ -621,14 +614,7 @@ pub fn setup_serial_device( out, Some(kick_stdin_read_evt), ))); - if let Err(e) = event_manager.add_subscriber(serial.clone()) { - // TODO: We just log this message, and immediately return Ok, instead of returning the - // actual error because this operation always fails with EPERM when adding a fd which - // has been redirected to /dev/null via dup2 (this may happen inside the jailer). - // Find a better solution to this (and think about the state of the serial device - // while we're at it). - warn!("Could not add serial input event to epoll: {:?}", e); - } + event_manager.add_subscriber(serial.clone()); Ok(serial) } @@ -765,7 +751,7 @@ pub fn configure_system_for_boot( } /// Attaches a VirtioDevice device to the device manager and event manager. -fn attach_virtio_device( +fn attach_virtio_device( event_manager: &mut EventManager, vmm: &mut Vmm, id: String, @@ -774,9 +760,7 @@ fn attach_virtio_device( ) -> std::result::Result<(), StartMicrovmError> { use self::StartMicrovmError::*; - event_manager - .add_subscriber(device.clone()) - .map_err(RegisterEvent)?; + event_manager.add_subscriber(device.clone()); // The device mutex mustn't be locked here otherwise it will deadlock. let device = MmioTransport::new(vmm.guest_memory().clone(), device); @@ -891,7 +875,6 @@ pub mod tests { use arch::DeviceType; use devices::virtio::{TYPE_BALLOON, TYPE_BLOCK, TYPE_VSOCK}; use kernel::cmdline::Cmdline; - use polly::event_manager::EventManager; use utils::tempfile::TempFile; pub(crate) struct CustomBlockConfig { @@ -1449,11 +1432,6 @@ pub mod tests { let err = OpenBlockDevice(io::Error::from_raw_os_error(0)); let _ = format!("{}{:?}", err, err); - - let err = RegisterEvent(EventManagerError::EpollCreate( - io::Error::from_raw_os_error(0), - )); - let _ = format!("{}{:?}", err, err); } #[test] diff --git a/src/vmm/src/device_manager/persist.rs b/src/vmm/src/device_manager/persist.rs index cb1ec422512..017602b2153 100644 --- a/src/vmm/src/device_manager/persist.rs +++ b/src/vmm/src/device_manager/persist.rs @@ -8,6 +8,7 @@ use std::result::Result; use std::sync::{Arc, Mutex}; use super::mmio::*; +use crate::EventManager; #[cfg(target_arch = "aarch64")] use arch::DeviceType; @@ -23,8 +24,8 @@ use devices::virtio::vsock::{Vsock, VsockError, VsockUnixBackend, VsockUnixBacke use devices::virtio::{ MmioTransport, VirtioDevice, TYPE_BALLOON, TYPE_BLOCK, TYPE_NET, TYPE_VSOCK, }; +use event_manager::{MutEventSubscriber, SubscriberOps}; use kvm_ioctls::VmFd; -use polly::event_manager::{Error as EventMgrError, EventManager, Subscriber}; use snapshot::Persist; use versionize::{VersionMap, Versionize, VersionizeError, VersionizeResult}; use versionize_derive::Versionize; @@ -35,7 +36,6 @@ use vm_memory::GuestMemoryMmap; pub enum Error { Balloon(BalloonError), Block(io::Error), - EventManager(EventMgrError), DeviceManager(super::mmio::Error), MmioTransport, #[cfg(target_arch = "aarch64")] @@ -283,7 +283,7 @@ impl<'a> Persist<'a> for MMIODeviceManager { } let mut restore_helper = |device: Arc>, - as_subscriber: Arc>, + as_subscriber: Arc>, id: &String, state: &MmioTransportState, slot: &MMIODeviceInfo, @@ -303,9 +303,8 @@ impl<'a> Persist<'a> for MMIODeviceManager { .register_mmio_virtio(vm, id.clone(), mmio_transport, slot) .map_err(Error::DeviceManager)?; - event_manager - .add_subscriber(as_subscriber) - .map_err(Error::EventManager) + event_manager.add_subscriber(as_subscriber); + Ok(()) }; if let Some(balloon_state) = &state.balloon_device { @@ -402,7 +401,6 @@ mod tests { use crate::vmm_config::net::NetworkInterfaceConfig; use crate::vmm_config::vsock::VsockDeviceConfig; use devices::virtio::block::CacheType; - use polly::event_manager::EventManager; use utils::tempfile::TempFile; impl PartialEq for ConnectedBalloonState { diff --git a/src/vmm/src/lib.rs b/src/vmm/src/lib.rs index 15b3fdb1953..d91197db618 100644 --- a/src/vmm/src/lib.rs +++ b/src/vmm/src/lib.rs @@ -58,15 +58,18 @@ use devices::virtio::{ TYPE_BLOCK, TYPE_NET, }; use devices::BusDevice; +use event_manager::{EventManager as BaseEventManager, EventOps, Events, MutEventSubscriber}; use logger::{error, info, warn, LoggerError, MetricsError, METRICS}; -use polly::event_manager::{EventManager, Subscriber}; use rate_limiter::BucketUpdate; use seccompiler::BpfProgram; use snapshot::Persist; -use utils::epoll::{EpollEvent, EventSet}; +use utils::epoll::EventSet; use utils::eventfd::EventFd; use vm_memory::{GuestMemory, GuestMemoryMmap, GuestMemoryRegion, GuestRegionMmap}; +/// Shorthand type for the EventManager flavour used by Firecracker. +pub type EventManager = BaseEventManager>>; + /// Vmm exit-code type. pub type ExitCode = i32; /// Success exit code. @@ -763,9 +766,9 @@ impl Drop for Vmm { } } -impl Subscriber for Vmm { +impl MutEventSubscriber for Vmm { /// Handle a read event (EPOLLIN). - fn process(&mut self, event: &EpollEvent, _: &mut EventManager) { + fn process(&mut self, event: Events, _: &mut EventOps) { let source = event.fd(); let event_set = event.event_set(); @@ -795,10 +798,9 @@ impl Subscriber for Vmm { } } - fn interest_list(&self) -> Vec { - vec![EpollEvent::new( - EventSet::IN, - self.vcpus_exit_evt.as_raw_fd() as u64, - )] + fn init(&mut self, ops: &mut EventOps) { + if let Err(e) = ops.add(Events::new(&self.vcpus_exit_evt, EventSet::IN)) { + error!("Failed to register vmm exit event: {}", e); + } } } diff --git a/src/vmm/src/persist.rs b/src/vmm/src/persist.rs index 170542f1012..c8d1e44c635 100644 --- a/src/vmm/src/persist.rs +++ b/src/vmm/src/persist.rs @@ -20,7 +20,7 @@ use crate::device_manager::persist::DeviceStates; use crate::memory_snapshot; use crate::memory_snapshot::{GuestMemoryState, SnapshotMemory}; use crate::version_map::FC_VERSION_TO_SNAP_VERSION; -use crate::{Error as VmmError, Vmm}; +use crate::{Error as VmmError, EventManager, Vmm}; #[cfg(target_arch = "x86_64")] use cpuid::common::{get_vendor_id_from_cpuid, get_vendor_id_from_host}; @@ -28,7 +28,6 @@ use crate::vmm_config::instance_info::InstanceInfo; #[cfg(target_arch = "aarch64")] use arch::regs::{get_manufacturer_id_from_host, get_manufacturer_id_from_state}; use logger::{error, info}; -use polly::event_manager::EventManager; use seccompiler::BpfThreadMap; use snapshot::Snapshot; use versionize::{VersionMap, Versionize, VersionizeResult}; @@ -498,7 +497,6 @@ mod tests { use crate::vmm_config::vsock::tests::default_config; use crate::Vmm; - use polly::event_manager::EventManager; use snapshot::Persist; use utils::{errno, tempfile::TempFile}; diff --git a/src/vmm/src/rpc_interface.rs b/src/vmm/src/rpc_interface.rs index e3ea72568de..64cb403dfff 100644 --- a/src/vmm/src/rpc_interface.rs +++ b/src/vmm/src/rpc_interface.rs @@ -11,7 +11,6 @@ use super::{ builder::build_microvm_for_boot, persist::create_snapshot, persist::restore_from_snapshot, resources::VmResources, Vmm, }; -use crate::builder::StartMicrovmError; use crate::persist::{CreateSnapshotError, LoadSnapshotError}; use crate::version_map::VERSION_MAP; use crate::vmm_config::balloon::{ @@ -31,9 +30,9 @@ use crate::vmm_config::net::{ use crate::vmm_config::snapshot::{CreateSnapshotParams, LoadSnapshotParams, SnapshotType}; use crate::vmm_config::vsock::{VsockConfigError, VsockDeviceConfig}; use crate::vmm_config::{self, RateLimiterUpdate}; +use crate::{builder::StartMicrovmError, EventManager}; use crate::{ExitCode, FC_EXIT_CODE_BAD_CONFIGURATION}; use logger::{info, update_metric_with_elapsed_time, METRICS}; -use polly::event_manager::EventManager; use seccompiler::BpfThreadMap; #[cfg(test)] use tests::{ diff --git a/src/vmm/src/utilities/test_utils/mod.rs b/src/vmm/src/utilities/test_utils/mod.rs index 721694e396a..51add829b8d 100644 --- a/src/vmm/src/utilities/test_utils/mod.rs +++ b/src/vmm/src/utilities/test_utils/mod.rs @@ -10,8 +10,7 @@ use crate::seccomp_filters::{get_filters, SeccompConfig}; use crate::utilities::mock_resources::{MockBootSourceConfig, MockVmConfig, MockVmResources}; use crate::vmm_config::boot_source::BootSourceConfig; use crate::vmm_config::instance_info::InstanceInfo; -use crate::Vmm; -use polly::event_manager::EventManager; +use crate::{EventManager, Vmm}; pub fn create_vmm(_kernel_image: Option<&str>, is_diff: bool) -> (Arc>, EventManager) { let mut event_manager = EventManager::new().unwrap(); diff --git a/src/vmm/tests/integration_tests.rs b/src/vmm/tests/integration_tests.rs index 8bddb2b1230..f3bd8865aef 100644 --- a/src/vmm/tests/integration_tests.rs +++ b/src/vmm/tests/integration_tests.rs @@ -5,21 +5,18 @@ use std::io::{Seek, SeekFrom}; use std::thread; use std::time::Duration; -use polly::event_manager::EventManager; use snapshot::Snapshot; use utils::tempfile::TempFile; -use vmm::builder::build_microvm_from_snapshot; -use vmm::builder::{build_microvm_for_boot, setup_serial_device}; -use vmm::persist::{snapshot_state_sanity_check, LoadSnapshotError, MicrovmState}; +use vmm::builder::{build_microvm_for_boot, build_microvm_from_snapshot, setup_serial_device}; +use vmm::persist::{self, snapshot_state_sanity_check, LoadSnapshotError, MicrovmState}; use vmm::resources::VmResources; use vmm::seccomp_filters::{get_filters, SeccompConfig}; use vmm::version_map::VERSION_MAP; use vmm::vmm_config::snapshot::{CreateSnapshotParams, SnapshotType}; -use vmm::{persist, FC_EXIT_CODE_OK}; +use vmm::{EventManager, FC_EXIT_CODE_OK}; use vmm::utilities::mock_devices::MockSerialInput; -use vmm::utilities::mock_resources::MockVmResources; -use vmm::utilities::mock_resources::NOISY_KERNEL_IMAGE; +use vmm::utilities::mock_resources::{MockVmResources, NOISY_KERNEL_IMAGE}; #[cfg(target_arch = "x86_64")] use vmm::utilities::test_utils::dirty_tracking_vmm; use vmm::utilities::test_utils::{create_vmm, default_vmm}; diff --git a/tests/integration_tests/build/test_coverage.py b/tests/integration_tests/build/test_coverage.py index 18ef259fca7..5544f6ada81 100644 --- a/tests/integration_tests/build/test_coverage.py +++ b/tests/integration_tests/build/test_coverage.py @@ -24,7 +24,7 @@ # this contains the frequency while on AMD it does not. # Checkout the cpuid crate. In the future other # differences may appear. -COVERAGE_DICT = {"Intel": 85.15, "AMD": 84.58, "ARM": 83.48} +COVERAGE_DICT = {"Intel": 85.15, "AMD": 84.52, "ARM": 83.36} PROC_MODEL = proc.proc_type()