vmm: add guest exit event path

Introduce a dedicated guest_exit_evt and a matching epoll dispatch
path for guest-triggered shutdowns.

This series is needed because managment software such as libvirt may
still need the Cloud Hypervisor process to stay alive after the guest
has shut down.
Today a guest-triggered shutdown can make the VMM disappear immediately,
which means the managment software can lose track of the VM run-state.

This must only apply to guest-triggered shutdowns. Fatal error paths
and other internal exit paths must keep using the existing VMM exit
handling.

For now GuestExit still calls vmm_shutdown(), so this commit only adds
the separate plumbing and keeps the current behavior unchanged.

On-behalf-of: SAP leander.kohler@sap.com
Signed-off-by: Leander Kohler <leander.kohler@cyberus-technology.de>
This commit is contained in:
Leander Kohler
2026-03-09 16:02:40 +01:00
committed by Rob Bradford
parent dc0e003be0
commit c698075157
3 changed files with 48 additions and 0 deletions
+35
View File
@@ -250,6 +250,7 @@ pub enum EpollDispatch {
Api = 2,
ActivateVirtioDevices = 3,
Debug = 4,
GuestExit = 5,
Unknown,
}
@@ -262,6 +263,7 @@ impl From<u64> for EpollDispatch {
2 => Api,
3 => ActivateVirtioDevices,
4 => Debug,
5 => GuestExit,
_ => Unknown,
}
}
@@ -608,6 +610,7 @@ pub struct Vmm {
epoll: EpollContext,
exit_evt: EventFd,
reset_evt: EventFd,
guest_exit_evt: EventFd,
api_evt: EventFd,
#[cfg(feature = "guest_debug")]
debug_evt: EventFd,
@@ -781,6 +784,7 @@ impl Vmm {
) -> Result<Self> {
let mut epoll = EpollContext::new().map_err(Error::Epoll)?;
let reset_evt = EventFd::new(EFD_NONBLOCK).map_err(Error::EventFdCreate)?;
let guest_exit_evt = EventFd::new(EFD_NONBLOCK).map_err(Error::EventFdCreate)?;
let activate_evt = EventFd::new(EFD_NONBLOCK).map_err(Error::EventFdCreate)?;
epoll
@@ -791,6 +795,10 @@ impl Vmm {
.add_event(&reset_evt, EpollDispatch::Reset)
.map_err(Error::Epoll)?;
epoll
.add_event(&guest_exit_evt, EpollDispatch::GuestExit)
.map_err(Error::Epoll)?;
epoll
.add_event(&activate_evt, EpollDispatch::ActivateVirtioDevices)
.map_err(Error::Epoll)?;
@@ -808,6 +816,7 @@ impl Vmm {
epoll,
exit_evt,
reset_evt,
guest_exit_evt,
api_evt,
#[cfg(feature = "guest_debug")]
debug_evt,
@@ -1109,6 +1118,9 @@ impl Vmm {
let reset_evt = self.reset_evt.try_clone().map_err(|e| {
MigratableError::MigrateReceive(anyhow!("Error cloning reset EventFd: {e}"))
})?;
let guest_exit_evt = self.guest_exit_evt.try_clone().map_err(|e| {
MigratableError::MigrateReceive(anyhow!("Error cloning guest exit EventFd: {e}"))
})?;
#[cfg(feature = "guest_debug")]
let debug_evt = self.vm_debug_evt.try_clone().map_err(|e| {
MigratableError::MigrateReceive(anyhow!("Error cloning debug EventFd: {e}"))
@@ -1128,6 +1140,7 @@ impl Vmm {
hypervisor_vm,
exit_evt,
reset_evt,
guest_exit_evt,
#[cfg(feature = "guest_debug")]
debug_evt,
&self.seccomp_action,
@@ -1569,6 +1582,10 @@ impl Vmm {
let exit_evt = self.exit_evt.try_clone().map_err(VmError::EventFdClone)?;
let reset_evt = self.reset_evt.try_clone().map_err(VmError::EventFdClone)?;
let guest_exit_evt = self
.guest_exit_evt
.try_clone()
.map_err(VmError::EventFdClone)?;
#[cfg(feature = "guest_debug")]
let debug_evt = self
.vm_debug_evt
@@ -1583,6 +1600,7 @@ impl Vmm {
vm_config,
exit_evt,
reset_evt,
guest_exit_evt,
#[cfg(feature = "guest_debug")]
debug_evt,
&self.seccomp_action,
@@ -1667,6 +1685,13 @@ impl Vmm {
self.reset_evt.read().map_err(Error::EventFdRead)?;
self.vm_reboot().map_err(Error::VmReboot)?;
}
EpollDispatch::GuestExit => {
info!("VM guest exit event");
self.guest_exit_evt.read().map_err(Error::EventFdRead)?;
self.vmm_shutdown().map_err(Error::VmmShutdown)?;
break 'outer;
}
EpollDispatch::ActivateVirtioDevices => {
if let Some(ref vm) = self.vm {
let count = self.activate_evt.read().map_err(Error::EventFdRead)?;
@@ -1775,6 +1800,10 @@ impl RequestHandler for Vmm {
if self.vm.is_none() {
let exit_evt = self.exit_evt.try_clone().map_err(VmError::EventFdClone)?;
let reset_evt = self.reset_evt.try_clone().map_err(VmError::EventFdClone)?;
let guest_exit_evt = self
.guest_exit_evt
.try_clone()
.map_err(VmError::EventFdClone)?;
#[cfg(feature = "guest_debug")]
let vm_debug_evt = self
.vm_debug_evt
@@ -1790,6 +1819,7 @@ impl RequestHandler for Vmm {
Arc::clone(vm_config),
exit_evt,
reset_evt,
guest_exit_evt,
#[cfg(feature = "guest_debug")]
vm_debug_evt,
&self.seccomp_action,
@@ -1955,6 +1985,10 @@ impl RequestHandler for Vmm {
let exit_evt = self.exit_evt.try_clone().map_err(VmError::EventFdClone)?;
let reset_evt = self.reset_evt.try_clone().map_err(VmError::EventFdClone)?;
let guest_exit_evt = self
.guest_exit_evt
.try_clone()
.map_err(VmError::EventFdClone)?;
#[cfg(feature = "guest_debug")]
let debug_evt = self
.vm_debug_evt
@@ -1980,6 +2014,7 @@ impl RequestHandler for Vmm {
config,
exit_evt,
reset_evt,
guest_exit_evt,
#[cfg(feature = "guest_debug")]
debug_evt,
&self.seccomp_action,