crosvm: vm_events: consolidate vm events into one framework.
crosvm waits on events like exit, reset, crash, guest panic etc and
uses eventfd to wait on these events. As of now, we have 4 eventfds
and may increase.
This is an attempt to consolidate all Vm events into one framework.
Use Tube instead of Event to get consistent behavior between OSes.
Implement a wrapper over Tube to have a consistent API for events.
BUG=None.
TEST=Built crosvm. Ran a minimal vm to panic and verified that crosvm
received the panic event. cargo test on devices.
Change-Id: I313d428de5e3ce3b879982f913918ec0a4a72c35
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/3480577
Tested-by: kokoro <noreply+kokoro@google.com>
Reviewed-by: Daniel Verkamp <dverkamp@chromium.org>
Commit-Queue: Vineeth Pillai <vineethrp@google.com>
Reviewed-by: Noah Gold <nkgold@google.com>
diff --git a/aarch64/src/lib.rs b/aarch64/src/lib.rs
index 18687af..daba095 100644
--- a/aarch64/src/lib.rs
+++ b/aarch64/src/lib.rs
@@ -14,7 +14,7 @@
get_serial_cmdline, GetSerialCmdlineError, MsrConfig, MsrExitHandlerError, RunnableLinuxVm,
VmComponents, VmImage,
};
-use base::{Event, MemoryMappingBuilder};
+use base::{Event, MemoryMappingBuilder, SendTube, Tube};
use devices::serial_device::{SerialHardware, SerialParameters};
use devices::{
Bus, BusDeviceObj, BusError, IrqChip, IrqChipAArch64, PciAddress, PciConfigMmio, PciDevice,
@@ -232,8 +232,7 @@
fn build_vm<V, Vcpu>(
mut components: VmComponents,
- _exit_evt: &Event,
- _reset_evt: &Event,
+ _vm_evt_wrtube: &SendTube,
system_allocator: &mut SystemAllocator,
serial_parameters: &BTreeMap<(SerialHardware, u8), SerialParameters>,
serial_jail: Option<Minijail>,
diff --git a/arch/src/lib.rs b/arch/src/lib.rs
index f91c779..ea2fbf6 100644
--- a/arch/src/lib.rs
+++ b/arch/src/lib.rs
@@ -20,7 +20,7 @@
use acpi_tables::aml::Aml;
use acpi_tables::sdt::SDT;
-use base::{syslog, AsRawDescriptor, AsRawDescriptors, Event, Tube};
+use base::{syslog, AsRawDescriptor, AsRawDescriptors, Event, SendTube, Tube};
use devices::virtio::VirtioDevice;
use devices::{
BarRange, Bus, BusDevice, BusDeviceObj, BusError, BusResumeDevice, HotPlugBus, IrqChip,
@@ -184,10 +184,8 @@
/// # Arguments
///
/// * `components` - Parts to use to build the VM.
- /// * `exit_evt` - Event used by sub-devices to request that crosvm exit because guest
- /// wants to stop/shut down.
- /// * `reset_evt` - Event used by sub-devices to request that crosvm exit because guest
- /// requested reset.
+ /// * `vm_evt_wrtube` - Tube used by sub-devices to request that crosvm exit because guest
+ /// wants to stop/shut down or requested reset.
/// * `system_allocator` - Allocator created by this trait's implementation of
/// `get_system_allocator_config`.
/// * `serial_parameters` - Definitions for how the serial devices should be configured.
@@ -199,8 +197,7 @@
/// * `irq_chip` - The IRQ chip implemention for the VM.
fn build_vm<V, Vcpu>(
components: VmComponents,
- exit_evt: &Event,
- reset_evt: &Event,
+ vm_evt_wrtube: &SendTube,
system_allocator: &mut SystemAllocator,
serial_parameters: &BTreeMap<(SerialHardware, u8), SerialParameters>,
serial_jail: Option<Minijail>,
diff --git a/base/src/lib.rs b/base/src/lib.rs
index fd941da..7f1d940 100644
--- a/base/src/lib.rs
+++ b/base/src/lib.rs
@@ -146,3 +146,12 @@
.encode_lower(&mut buf)
.to_owned()
}
+
+use serde::{Deserialize, Serialize};
+#[derive(Clone, Copy, Serialize, Deserialize, Debug, PartialEq)]
+pub enum VmEventType {
+ Exit,
+ Reset,
+ Crash,
+ Panic(u8),
+}
diff --git a/devices/src/acpi.rs b/devices/src/acpi.rs
index 46143b4..5ce06c5 100644
--- a/devices/src/acpi.rs
+++ b/devices/src/acpi.rs
@@ -4,7 +4,9 @@
use crate::{BusAccessInfo, BusDevice, BusResumeDevice, IrqLevelEvent};
use acpi_tables::{aml, aml::Aml};
-use base::{error, info, warn, Error as SysError, Event, PollToken, WaitContext};
+use base::{
+ error, info, warn, Error as SysError, Event, PollToken, SendTube, VmEventType, WaitContext,
+};
use base::{AcpiNotifyEvent, NetlinkGenericSocket};
use std::collections::BTreeMap;
use std::sync::Arc;
@@ -63,7 +65,7 @@
kill_evt: Option<Event>,
worker_thread: Option<thread::JoinHandle<()>>,
suspend_evt: Event,
- exit_evt: Event,
+ exit_evt_wrtube: SendTube,
pm1: Arc<Mutex<Pm1Resource>>,
gpe0: Arc<Mutex<GpeResource>>,
}
@@ -77,7 +79,7 @@
sci_evt: IrqLevelEvent,
#[cfg(feature = "direct")] direct_gpe_info: Option<(IrqLevelEvent, &[u32])>,
suspend_evt: Event,
- exit_evt: Event,
+ exit_evt_wrtube: SendTube,
) -> ACPIPMResource {
let pm1 = Pm1Resource {
status: 0,
@@ -108,7 +110,7 @@
kill_evt: None,
worker_thread: None,
suspend_evt,
- exit_evt,
+ exit_evt_wrtube,
pm1: Arc::new(Mutex::new(pm1)),
gpe0: Arc::new(Mutex::new(gpe0)),
}
@@ -777,7 +779,7 @@
if (val & BITMASK_PM1CNT_SLEEP_ENABLE) != 0 {
// only support S5 in direct mode
#[cfg(feature = "direct")]
- if let Err(e) = self.exit_evt.write(1) {
+ if let Err(e) = self.exit_evt_wrtube.send::<VmEventType>(&VmEventType::Exit) {
error!("ACPIPM: failed to trigger exit event: {}", e);
}
#[cfg(not(feature = "direct"))]
@@ -788,7 +790,9 @@
}
}
SLEEP_TYPE_S5 => {
- if let Err(e) = self.exit_evt.write(1) {
+ if let Err(e) =
+ self.exit_evt_wrtube.send::<VmEventType>(&VmEventType::Exit)
+ {
error!("ACPIPM: failed to trigger exit event: {}", e);
}
}
diff --git a/devices/src/i8042.rs b/devices/src/i8042.rs
index 92ea2a1..4085476 100644
--- a/devices/src/i8042.rs
+++ b/devices/src/i8042.rs
@@ -2,19 +2,19 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-use base::{error, Event};
+use base::{error, SendTube, VmEventType};
use crate::{BusAccessInfo, BusDevice};
/// A i8042 PS/2 controller that emulates just enough to shutdown the machine.
pub struct I8042Device {
- reset_evt: Event,
+ reset_evt_wrtube: SendTube,
}
impl I8042Device {
/// Constructs a i8042 device that will signal the given event when the guest requests it.
- pub fn new(reset_evt: Event) -> I8042Device {
- I8042Device { reset_evt }
+ pub fn new(reset_evt_wrtube: SendTube) -> I8042Device {
+ I8042Device { reset_evt_wrtube }
}
}
@@ -37,7 +37,10 @@
fn write(&mut self, info: BusAccessInfo, data: &[u8]) {
if data.len() == 1 && data[0] == 0xfe && info.address == 0x64 {
- if let Err(e) = self.reset_evt.write(1) {
+ if let Err(e) = self
+ .reset_evt_wrtube
+ .send::<VmEventType>(&VmEventType::Reset)
+ {
error!("failed to trigger i8042 reset event: {}", e);
}
}
diff --git a/devices/src/pci/pci_root.rs b/devices/src/pci/pci_root.rs
index 517964e..7da5ee0 100644
--- a/devices/src/pci/pci_root.rs
+++ b/devices/src/pci/pci_root.rs
@@ -7,7 +7,7 @@
use std::ops::Bound::Included;
use std::sync::{Arc, Weak};
-use base::{error, Event, RawDescriptor};
+use base::{error, RawDescriptor, SendTube, VmEventType};
use sync::Mutex;
use crate::pci::pci_configuration::{
@@ -248,18 +248,18 @@
pci_root: Arc<Mutex<PciRoot>>,
/// Current address to read/write from (0xcf8 register, litte endian).
config_address: u32,
- /// Event to signal that the quest requested reset via writing to 0xcf9 register.
- reset_evt: Event,
+ /// Tube to signal that the guest requested reset via writing to 0xcf9 register.
+ reset_evt_wrtube: SendTube,
}
impl PciConfigIo {
const REGISTER_BITS_NUM: usize = 8;
- pub fn new(pci_root: Arc<Mutex<PciRoot>>, reset_evt: Event) -> Self {
+ pub fn new(pci_root: Arc<Mutex<PciRoot>>, reset_evt_wrtube: SendTube) -> Self {
PciConfigIo {
pci_root,
config_address: 0,
- reset_evt,
+ reset_evt_wrtube,
}
}
@@ -344,7 +344,10 @@
// `offset` is relative to 0xcf8
match info.offset {
_o @ 1 if data.len() == 1 && data[0] & PCI_RESET_CPU_BIT != 0 => {
- if let Err(e) = self.reset_evt.write(1) {
+ if let Err(e) = self
+ .reset_evt_wrtube
+ .send::<VmEventType>(&VmEventType::Reset)
+ {
error!("failed to trigger PCI 0xcf9 reset event: {}", e);
}
}
diff --git a/devices/src/pci/pvpanic.rs b/devices/src/pci/pvpanic.rs
index 5265a9f..bac36ee 100644
--- a/devices/src/pci/pvpanic.rs
+++ b/devices/src/pci/pvpanic.rs
@@ -15,7 +15,7 @@
use std::fmt;
use base::RawDescriptor;
-use base::{self, error, Tube};
+use base::{self, error, SendTube, VmEventType};
use resources::{Alloc, MmioType, SystemAllocator};
use crate::pci::pci_configuration::{
@@ -69,11 +69,11 @@
pub struct PvPanicPciDevice {
pci_address: Option<PciAddress>,
config_regs: PciConfiguration,
- evt_tube: Tube,
+ evt_wrtube: SendTube,
}
impl PvPanicPciDevice {
- pub fn new(evt_tube: Tube) -> PvPanicPciDevice {
+ pub fn new(evt_wrtube: SendTube) -> PvPanicPciDevice {
let config_regs = PciConfiguration::new(
PCI_VENDOR_ID_REDHAT,
PCI_DEVICE_ID_REDHAT_PVPANIC,
@@ -89,7 +89,7 @@
Self {
pci_address: None,
config_regs,
- evt_tube,
+ evt_wrtube,
}
}
}
@@ -182,8 +182,12 @@
if addr != mmio_addr || data.len() != 1 {
return;
}
- if let Err(e) = self.evt_tube.send::<u8>(&data[0]) {
- error!("Failed to send to the panic event: {}", e);
+
+ if let Err(e) = self
+ .evt_wrtube
+ .send::<VmEventType>(&VmEventType::Panic(data[0]))
+ {
+ error!("Failed to write to the event tube: {}", e);
}
}
}
@@ -191,6 +195,7 @@
#[cfg(test)]
mod test {
use super::*;
+ use base::Tube;
use resources::{MemRegion, SystemAllocator, SystemAllocatorConfig};
#[test]
@@ -217,7 +222,7 @@
)
.unwrap();
- let (evt_rdtube, evt_wrtube) = Tube::pair().unwrap();
+ let (evt_wrtube, evt_rdtube) = Tube::directional_pair().unwrap();
let mut device = PvPanicPciDevice::new(evt_wrtube);
assert!(device.allocate_address(&mut allocator).is_ok());
@@ -239,7 +244,7 @@
device.write_bar(mmio_addr, &data);
// Verify the event
- let val = evt_rdtube.recv::<u8>().unwrap();
- assert_eq!(val, PVPANIC_CRASH_LOADED);
+ let val = evt_rdtube.recv::<VmEventType>().unwrap();
+ assert_eq!(val, VmEventType::Panic(PVPANIC_CRASH_LOADED));
}
}
diff --git a/devices/src/virtio/gpu/mod.rs b/devices/src/virtio/gpu/mod.rs
index fba1925..827a3cb 100644
--- a/devices/src/virtio/gpu/mod.rs
+++ b/devices/src/virtio/gpu/mod.rs
@@ -23,7 +23,7 @@
use base::{
debug, error, warn, AsRawDescriptor, Event, ExternalMapping, PollToken, RawDescriptor,
- SafeDescriptor, Tube, WaitContext,
+ SafeDescriptor, SendTube, Tube, VmEventType, WaitContext,
};
use data_model::*;
@@ -795,7 +795,7 @@
struct Worker {
interrupt: Arc<Interrupt>,
- exit_evt: Event,
+ exit_evt_wrtube: SendTube,
mem: GuestMemory,
ctrl_queue: SharedQueueReader,
ctrl_evt: Event,
@@ -914,7 +914,7 @@
Token::Display => {
let close_requested = self.state.process_display();
if close_requested {
- let _ = self.exit_evt.write(1);
+ let _ = self.exit_evt_wrtube.send::<VmEventType>(&VmEventType::Exit);
}
}
Token::ResourceBridge { index } => {
@@ -1002,7 +1002,7 @@
}
pub struct Gpu {
- exit_evt: Event,
+ exit_evt_wrtube: SendTube,
gpu_device_tube: Option<Tube>,
resource_bridges: Vec<Tube>,
event_devices: Vec<EventDevice>,
@@ -1023,7 +1023,7 @@
impl Gpu {
pub fn new(
- exit_evt: Event,
+ exit_evt_wrtube: SendTube,
gpu_device_tube: Option<Tube>,
resource_bridges: Vec<Tube>,
display_backends: Vec<DisplayBackend>,
@@ -1090,7 +1090,7 @@
.set_rutabaga_channels(rutabaga_channels_opt);
Gpu {
- exit_evt,
+ exit_evt_wrtube,
gpu_device_tube,
resource_bridges,
event_devices,
@@ -1215,7 +1215,7 @@
keep_rds.push(render_server_fd.as_raw_descriptor());
}
- keep_rds.push(self.exit_evt.as_raw_descriptor());
+ keep_rds.push(self.exit_evt_wrtube.as_raw_descriptor());
for bridge in &self.resource_bridges {
keep_rds.push(bridge.as_raw_descriptor());
}
@@ -1281,10 +1281,10 @@
return;
}
- let exit_evt = match self.exit_evt.try_clone() {
+ let exit_evt_wrtube = match self.exit_evt_wrtube.try_clone() {
Ok(e) => e,
Err(e) => {
- error!("error cloning exit event: {}", e);
+ error!("error cloning exit tube: {}", e);
return;
}
};
@@ -1347,7 +1347,7 @@
Worker {
interrupt: irq,
- exit_evt,
+ exit_evt_wrtube,
mem,
ctrl_queue: ctrl_queue.clone(),
ctrl_evt,
diff --git a/devices/src/virtio/vhost/user/device/gpu.rs b/devices/src/virtio/vhost/user/device/gpu.rs
index b09e4b6..3a90d93 100644
--- a/devices/src/virtio/vhost/user/device/gpu.rs
+++ b/devices/src/virtio/vhost/user/device/gpu.rs
@@ -439,7 +439,11 @@
.detach();
}
- let exit_evt = Event::new().context("failed to create Event")?;
+ // TODO(b/232344535): Read side of the tube is ignored currently.
+ // Complete the implementation by polling `exit_evt_rdtube` and
+ // kill the sibling VM.
+ let (exit_evt_wrtube, _) =
+ Tube::directional_pair().context("failed to create vm event tube")?;
// Initialized later.
let gpu_device_tube = None;
@@ -465,7 +469,7 @@
let channels = wayland_paths;
let gpu = Rc::new(RefCell::new(Gpu::new(
- exit_evt,
+ exit_evt_wrtube,
gpu_device_tube,
Vec::new(), // resource_bridges, handled separately by us
display_backends,
diff --git a/src/linux/gpu.rs b/src/linux/gpu.rs
index 7fe860a..f16c9c9 100644
--- a/src/linux/gpu.rs
+++ b/src/linux/gpu.rs
@@ -153,7 +153,7 @@
pub fn create_gpu_device(
cfg: &Config,
- exit_evt: &Event,
+ exit_evt_wrtube: &SendTube,
gpu_device_tube: Tube,
resource_bridges: Vec<Tube>,
wayland_socket_path: Option<&PathBuf>,
@@ -182,7 +182,9 @@
}
let dev = virtio::Gpu::new(
- exit_evt.try_clone().context("failed to clone event")?,
+ exit_evt_wrtube
+ .try_clone()
+ .context("failed to clone tube")?,
Some(gpu_device_tube),
resource_bridges,
display_backends,
diff --git a/src/linux/mod.rs b/src/linux/mod.rs
index d5f3ec5..7cf99dc 100644
--- a/src/linux/mod.rs
+++ b/src/linux/mod.rs
@@ -98,7 +98,7 @@
cfg: &Config,
vm: &mut impl Vm,
resources: &mut SystemAllocator,
- _exit_evt: &Event,
+ vm_evt_wrtube: &SendTube,
wayland_device_tube: Tube,
gpu_device_tube: Tube,
vhost_user_gpu_tubes: Vec<(Tube, Tube, Tube)>,
@@ -233,7 +233,7 @@
devs.push(create_gpu_device(
cfg,
- _exit_evt,
+ vm_evt_wrtube,
gpu_device_tube,
resource_bridges,
// Use the unnamed socket for GPU display screens.
@@ -461,8 +461,7 @@
cfg: &Config,
vm: &mut impl Vm,
resources: &mut SystemAllocator,
- exit_evt: &Event,
- panic_wrtube: Tube,
+ vm_evt_wrtube: &SendTube,
iommu_attached_endpoints: &mut BTreeMap<u32, Arc<Mutex<Box<dyn MemoryMapperTrait>>>>,
control_tubes: &mut Vec<TaggedControlTube>,
wayland_device_tube: Tube,
@@ -585,7 +584,7 @@
cfg,
vm,
resources,
- exit_evt,
+ vm_evt_wrtube,
wayland_device_tube,
gpu_device_tube,
vhost_user_gpu_tubes,
@@ -631,7 +630,11 @@
devices.push((Box::new(StubPciDevice::new(params)), None));
}
- devices.push((Box::new(PvPanicPciDevice::new(panic_wrtube)), None));
+ devices.push((
+ Box::new(PvPanicPciDevice::new(vm_evt_wrtube.try_clone()?)),
+ None,
+ ));
+
Ok(devices)
}
@@ -1218,10 +1221,8 @@
vvu_proxy_device_tubes.push(vvu_proxy_device_tube);
}
- let exit_evt = Event::new().context("failed to create event")?;
- let reset_evt = Event::new().context("failed to create event")?;
- let crash_evt = Event::new().context("failed to create event")?;
- let (panic_rdtube, panic_wrtube) = Tube::pair().context("failed to create tube")?;
+ let (vm_evt_wrtube, vm_evt_rdtube) =
+ Tube::directional_pair().context("failed to create vm event tube")?;
let pstore_size = components.pstore.as_ref().map(|pstore| pstore.size as u64);
let mut sys_allocator = SystemAllocator::new(
@@ -1318,8 +1319,7 @@
&cfg,
&mut vm,
&mut sys_allocator,
- &exit_evt,
- panic_wrtube,
+ &vm_evt_wrtube,
&mut iommu_attached_endpoints,
&mut control_tubes,
wayland_device_tube,
@@ -1418,8 +1418,7 @@
#[cfg_attr(not(feature = "direct"), allow(unused_mut))]
let mut linux = Arch::build_vm::<V, Vcpu>(
components,
- &exit_evt,
- &reset_evt,
+ &vm_evt_wrtube,
&mut sys_allocator,
&cfg.serial_parameters,
simple_jail(&cfg.jail_config, "serial")?,
@@ -1484,10 +1483,8 @@
&disk_host_tubes,
#[cfg(feature = "usb")]
usb_control_tube,
- exit_evt,
- reset_evt,
- crash_evt,
- panic_rdtube,
+ vm_evt_rdtube,
+ vm_evt_wrtube,
sigchld_fd,
Arc::clone(&map_request),
gralloc,
@@ -1669,10 +1666,8 @@
balloon_host_tube: Option<Tube>,
disk_host_tubes: &[Tube],
#[cfg(feature = "usb")] usb_control_tube: Tube,
- exit_evt: Event,
- reset_evt: Event,
- crash_evt: Event,
- panic_rdtube: Tube,
+ vm_evt_rdtube: RecvTube,
+ vm_evt_wrtube: SendTube,
sigchld_fd: SignalFd,
map_request: Arc<Mutex<Option<ExternalMapping>>>,
mut gralloc: RutabagaGralloc,
@@ -1681,10 +1676,7 @@
) -> Result<ExitState> {
#[derive(PollToken)]
enum Token {
- Exit,
- Reset,
- Crash,
- Panic,
+ VmEvent,
Suspend,
ChildSignal,
IrqFd { index: IrqEventIndex },
@@ -1698,12 +1690,9 @@
.expect("failed to set terminal raw mode");
let wait_ctx = WaitContext::build_with(&[
- (&exit_evt, Token::Exit),
- (&reset_evt, Token::Reset),
- (&crash_evt, Token::Crash),
- (&panic_rdtube, Token::Panic),
(&linux.suspend_evt, Token::Suspend),
(&sigchld_fd, Token::ChildSignal),
+ (&vm_evt_rdtube, Token::VmEvent),
])
.context("failed to add descriptor to wait context")?;
@@ -1808,9 +1797,9 @@
linux.has_bios,
(*linux.io_bus).clone(),
(*linux.mmio_bus).clone(),
- exit_evt.try_clone().context("failed to clone event")?,
- reset_evt.try_clone().context("failed to clone event")?,
- crash_evt.try_clone().context("failed to clone event")?,
+ vm_evt_wrtube
+ .try_clone()
+ .context("failed to clone vm event tube")?,
linux.vm.check_capability(VmCap::PvClockSuspend),
from_main_channel,
use_hypervisor_signals,
@@ -1869,37 +1858,38 @@
let mut vm_control_indices_to_remove = Vec::new();
for event in events.iter().filter(|e| e.is_readable) {
match event.token {
- Token::Exit => {
- info!("vcpu requested shutdown");
- break 'wait;
- }
- Token::Reset => {
- info!("vcpu requested reset");
- exit_state = ExitState::Reset;
- break 'wait;
- }
- Token::Crash => {
- info!("vcpu crashed");
- exit_state = ExitState::Crash;
- break 'wait;
- }
- Token::Panic => {
+ Token::VmEvent => {
let mut break_to_wait: bool = true;
- match panic_rdtube.recv::<u8>() {
- Ok(panic_code) => {
- let panic_code = PvPanicCode::from_u8(panic_code);
- info!("Guest reported panic [Code: {}]", panic_code);
- if panic_code == PvPanicCode::CrashLoaded {
- // VM is booting to crash kernel.
- break_to_wait = false;
+ match vm_evt_rdtube.recv::<VmEventType>() {
+ Ok(vm_event) => match vm_event {
+ VmEventType::Exit => {
+ info!("vcpu requested shutdown");
+ exit_state = ExitState::Stop;
}
- }
+ VmEventType::Reset => {
+ info!("vcpu requested reset");
+ exit_state = ExitState::Reset;
+ }
+ VmEventType::Crash => {
+ info!("vcpu crashed");
+ exit_state = ExitState::Crash;
+ }
+ VmEventType::Panic(panic_code) => {
+ let panic_code = PvPanicCode::from_u8(panic_code);
+ info!("Guest reported panic [Code: {}]", panic_code);
+ if panic_code == PvPanicCode::CrashLoaded {
+ // VM is booting to crash kernel.
+ break_to_wait = false;
+ } else {
+ exit_state = ExitState::GuestPanic;
+ }
+ }
+ },
Err(e) => {
- warn!("failed to recv panic event: {} ", e);
+ warn!("failed to recv VmEvent: {}", e);
}
}
if break_to_wait {
- exit_state = ExitState::GuestPanic;
break 'wait;
}
}
diff --git a/src/linux/vcpu.rs b/src/linux/vcpu.rs
index 5dce07c..798ac8e 100644
--- a/src/linux/vcpu.rs
+++ b/src/linux/vcpu.rs
@@ -535,9 +535,7 @@
has_bios: bool,
mut io_bus: Bus,
mut mmio_bus: Bus,
- exit_evt: Event,
- reset_evt: Event,
- crash_evt: Event,
+ vm_evt_wrtube: SendTube,
requires_pvclock_ctrl: bool,
from_main_tube: mpsc::Receiver<VcpuControl>,
use_hypervisor_signals: bool,
@@ -557,100 +555,97 @@
thread::Builder::new()
.name(format!("crosvm_vcpu{}", cpu_id))
.spawn(move || {
- // The VCPU thread must trigger either `exit_evt` or `reset_event` in all paths. A
- // `ScopedEvent`'s Drop implementation ensures that the `exit_evt` will be sent if
- // anything happens before we get to writing the final event.
- let scoped_exit_evt = ScopedEvent::from(exit_evt);
+ // Having a closure returning ExitState guarentees that we
+ // send a VmEventType on all code paths after the closure
+ // returns.
+ let vcpu_fn = || -> ExitState {
+ #[cfg(all(target_arch = "x86_64", feature = "gdb"))]
+ let guest_mem = vm.get_memory().clone();
+ let runnable_vcpu = runnable_vcpu(
+ cpu_id,
+ vcpu_id,
+ vcpu,
+ vm,
+ irq_chip.as_mut(),
+ vcpu_count,
+ run_rt && !delay_rt,
+ vcpu_affinity,
+ no_smt,
+ has_bios,
+ use_hypervisor_signals,
+ enable_per_vm_core_scheduling,
+ host_cpu_topology,
+ itmt,
+ vcpu_cgroup_tasks_file,
+ );
- #[cfg(all(target_arch = "x86_64", feature = "gdb"))]
- let guest_mem = vm.get_memory().clone();
- let runnable_vcpu = runnable_vcpu(
- cpu_id,
- vcpu_id,
- vcpu,
- vm,
- irq_chip.as_mut(),
- vcpu_count,
- run_rt && !delay_rt,
- vcpu_affinity,
- no_smt,
- has_bios,
- use_hypervisor_signals,
- enable_per_vm_core_scheduling,
- host_cpu_topology,
- itmt,
- vcpu_cgroup_tasks_file,
- );
-
- // Add MSR handlers after CPU affinity setting.
- // This avoids redundant MSR file fd creation.
- let mut msr_handlers = MsrHandlers::new();
- if !userspace_msr.is_empty() {
- userspace_msr.iter().for_each(|(index, msr_config)| {
- if let Err(e) = msr_handlers.add_handler(*index, msr_config.clone(), cpu_id) {
- error!("failed to add msr handler {}: {:#}", cpu_id, e);
- return;
- };
- });
- }
-
- start_barrier.wait();
-
- let (vcpu, vcpu_run_handle) = match runnable_vcpu {
- Ok(v) => v,
- Err(e) => {
- error!("failed to start vcpu {}: {:#}", cpu_id, e);
- return;
+ // Add MSR handlers after CPU affinity setting.
+ // This avoids redundant MSR file fd creation.
+ let mut msr_handlers = MsrHandlers::new();
+ if !userspace_msr.is_empty() {
+ userspace_msr.iter().for_each(|(index, msr_config)| {
+ if let Err(e) = msr_handlers.add_handler(*index, msr_config.clone(), cpu_id)
+ {
+ error!("failed to add msr handler {}: {:#}", cpu_id, e);
+ };
+ });
}
+
+ start_barrier.wait();
+
+ let (vcpu, vcpu_run_handle) = match runnable_vcpu {
+ Ok(v) => v,
+ Err(e) => {
+ error!("failed to start vcpu {}: {:#}", cpu_id, e);
+ return ExitState::Stop;
+ }
+ };
+
+ #[allow(unused_mut)]
+ let mut run_mode = VmRunMode::Running;
+ #[cfg(all(target_arch = "x86_64", feature = "gdb"))]
+ if to_gdb_tube.is_some() {
+ // Wait until a GDB client attaches
+ run_mode = VmRunMode::Breakpoint;
+ }
+
+ mmio_bus.set_access_id(cpu_id);
+ io_bus.set_access_id(cpu_id);
+
+ vcpu_loop(
+ run_mode,
+ cpu_id,
+ vcpu,
+ vcpu_run_handle,
+ irq_chip,
+ run_rt,
+ delay_rt,
+ io_bus,
+ mmio_bus,
+ requires_pvclock_ctrl,
+ from_main_tube,
+ use_hypervisor_signals,
+ privileged_vm,
+ #[cfg(all(target_arch = "x86_64", feature = "gdb"))]
+ to_gdb_tube,
+ #[cfg(all(target_arch = "x86_64", feature = "gdb"))]
+ guest_mem,
+ msr_handlers,
+ )
};
- #[allow(unused_mut)]
- let mut run_mode = VmRunMode::Running;
- #[cfg(all(target_arch = "x86_64", feature = "gdb"))]
- if to_gdb_tube.is_some() {
- // Wait until a GDB client attaches
- run_mode = VmRunMode::Breakpoint;
- }
-
- mmio_bus.set_access_id(cpu_id);
- io_bus.set_access_id(cpu_id);
-
- let exit_reason = vcpu_loop(
- run_mode,
- cpu_id,
- vcpu,
- vcpu_run_handle,
- irq_chip,
- run_rt,
- delay_rt,
- io_bus,
- mmio_bus,
- requires_pvclock_ctrl,
- from_main_tube,
- use_hypervisor_signals,
- privileged_vm,
- #[cfg(all(target_arch = "x86_64", feature = "gdb"))]
- to_gdb_tube,
- #[cfg(all(target_arch = "x86_64", feature = "gdb"))]
- guest_mem,
- msr_handlers,
- );
-
- let exit_evt = scoped_exit_evt.into();
- let final_event = match exit_reason {
- ExitState::Stop => Some(exit_evt),
- ExitState::Reset => Some(reset_evt),
- ExitState::Crash => Some(crash_evt),
+ let final_event_data = match vcpu_fn() {
+ ExitState::Stop => VmEventType::Exit,
+ ExitState::Reset => VmEventType::Reset,
+ ExitState::Crash => VmEventType::Crash,
// vcpu_loop doesn't exit with GuestPanic.
- ExitState::GuestPanic => None,
+ ExitState::GuestPanic => unreachable!(),
};
- if let Some(final_event) = final_event {
- if let Err(e) = final_event.write(1) {
- error!(
- "failed to send final event {:?} on vcpu {}: {}",
- final_event, cpu_id, e
- )
- }
+ if let Err(e) = vm_evt_wrtube.send::<VmEventType>(&final_event_data) {
+ error!(
+ "failed to send final event {:?} on vcpu {}: {}",
+ final_event_data, cpu_id, e
+ )
}
})
.context("failed to spawn VCPU thread")
diff --git a/x86_64/src/lib.rs b/x86_64/src/lib.rs
index 7ae7e7f..2fe9ad7 100644
--- a/x86_64/src/lib.rs
+++ b/x86_64/src/lib.rs
@@ -52,7 +52,6 @@
use once_cell::sync::OnceCell;
use std::arch::x86_64::__cpuid;
use std::collections::BTreeMap;
-use std::convert::TryFrom;
use std::ffi::{CStr, CString};
use std::fs::File;
use std::io::{self, Seek};
@@ -66,7 +65,7 @@
get_serial_cmdline, GetSerialCmdlineError, MsrAction, MsrConfig, MsrRWType, MsrValueFrom,
RunnableLinuxVm, VmComponents, VmImage,
};
-use base::{warn, Event};
+use base::{warn, Event, SendTube, TubeError};
use devices::serial_device::{SerialHardware, SerialParameters};
use devices::{
BusDeviceObj, BusResumeDevice, IrqChip, IrqChipX86_64, PciAddress, PciConfigIo, PciConfigMmio,
@@ -99,6 +98,8 @@
CloneEvent(base::Error),
#[error("failed to clone IRQ chip: {0}")]
CloneIrqChip(base::Error),
+ #[error("unable to clone a Tube: {0}")]
+ CloneTube(TubeError),
#[error("the given kernel command line was invalid: {0}")]
Cmdline(kernel_cmdline::Error),
#[error("failed to configure hotplugged pci device: {0}")]
@@ -498,8 +499,7 @@
fn build_vm<V, Vcpu>(
mut components: VmComponents,
- exit_evt: &Event,
- reset_evt: &Event,
+ vm_evt_wrtube: &SendTube,
system_allocator: &mut SystemAllocator,
serial_parameters: &BTreeMap<(SerialHardware, u8), SerialParameters>,
serial_jail: Option<Minijail>,
@@ -576,7 +576,7 @@
pci.lock().enable_pcie_cfg_mmio(read_pcie_cfg_mmio_start());
let pci_cfg = PciConfigIo::new(
pci.clone(),
- reset_evt.try_clone().map_err(Error::CloneEvent)?,
+ vm_evt_wrtube.try_clone().map_err(Error::CloneTube)?,
);
let pci_bus = Arc::new(Mutex::new(pci_cfg));
io_bus.insert(pci_bus, 0xcf8, 0x8).unwrap();
@@ -606,7 +606,7 @@
Self::setup_legacy_devices(
&io_bus,
irq_chip.pit_uses_speaker_port(),
- reset_evt.try_clone().map_err(Error::CloneEvent)?,
+ vm_evt_wrtube.try_clone().map_err(Error::CloneTube)?,
components.memory_size,
)?;
}
@@ -627,7 +627,7 @@
&io_bus,
system_allocator,
suspend_evt.try_clone().map_err(Error::CloneEvent)?,
- exit_evt.try_clone().map_err(Error::CloneEvent)?,
+ vm_evt_wrtube.try_clone().map_err(Error::CloneTube)?,
components.acpi_sdts,
#[cfg(feature = "direct")]
&components.direct_gpe,
@@ -1293,12 +1293,12 @@
///
/// * - `io_bus` - the IO bus object
/// * - `pit_uses_speaker_port` - does the PIT use port 0x61 for the PC speaker
- /// * - `reset_evt` - the event object which should receive exit events
+ /// * - `vm_evt_wrtube` - Tube for sending exit events
/// * - `mem_size` - the size in bytes of physical ram for the guest
fn setup_legacy_devices(
io_bus: &devices::Bus,
pit_uses_speaker_port: bool,
- reset_evt: Event,
+ vm_evt_wrtube: SendTube,
mem_size: u64,
) -> Result<()> {
let mem_regions = arch_memory_regions(mem_size, None);
@@ -1324,7 +1324,7 @@
.unwrap();
let i8042 = Arc::new(Mutex::new(devices::I8042Device::new(
- reset_evt.try_clone().map_err(Error::CloneEvent)?,
+ vm_evt_wrtube.try_clone().map_err(Error::CloneTube)?,
)));
if pit_uses_speaker_port {
@@ -1354,7 +1354,7 @@
io_bus: &devices::Bus,
resources: &mut SystemAllocator,
suspend_evt: Event,
- exit_evt: Event,
+ vm_evt_wrtube: SendTube,
sdts: Vec<SDT>,
#[cfg(feature = "direct")] direct_gpe: &[u32],
irq_chip: &mut dyn IrqChip,
@@ -1429,7 +1429,7 @@
#[cfg(feature = "direct")]
direct_gpe_info,
suspend_evt,
- exit_evt,
+ vm_evt_wrtube,
);
pmresource.to_aml_bytes(&mut amls);
pmresource.start();
diff --git a/x86_64/src/test_integration.rs b/x86_64/src/test_integration.rs
index 1a206cd..0e298d9 100644
--- a/x86_64/src/test_integration.rs
+++ b/x86_64/src/test_integration.rs
@@ -124,7 +124,7 @@
let mmio_bus = Arc::new(devices::Bus::new());
let io_bus = Arc::new(devices::Bus::new());
- let exit_evt = Event::new().unwrap();
+ let (exit_evt_wrtube, _) = Tube::directional_pair().unwrap();
let mut control_tubes = vec![TaggedControlTube::VmIrq(irqchip_tube)];
// Create one control socket per disk.
@@ -153,13 +153,14 @@
)
.unwrap();
let pci = Arc::new(Mutex::new(pci));
- let pci_bus = Arc::new(Mutex::new(PciConfigIo::new(pci, Event::new().unwrap())));
+ let (pcibus_exit_evt_wrtube, _) = Tube::directional_pair().unwrap();
+ let pci_bus = Arc::new(Mutex::new(PciConfigIo::new(pci, pcibus_exit_evt_wrtube)));
io_bus.insert(pci_bus, 0xcf8, 0x8).unwrap();
X8664arch::setup_legacy_devices(
&io_bus,
irq_chip.pit_uses_speaker_port(),
- exit_evt.try_clone().unwrap(),
+ exit_evt_wrtube.try_clone().unwrap(),
memory_size,
)
.unwrap();
@@ -204,7 +205,9 @@
suspend_evt
.try_clone()
.expect("unable to clone suspend_evt"),
- exit_evt.try_clone().expect("unable to clone exit_evt"),
+ exit_evt_wrtube
+ .try_clone()
+ .expect("unable to clone exit_evt_wrtube"),
Default::default(),
&mut irq_chip,
X86_64_SCI_IRQ,