diff --git a/lib/propolis/src/hw/chipset/i440fx.rs b/lib/propolis/src/hw/chipset/i440fx.rs index a3566d96c..5f874c10c 100644 --- a/lib/propolis/src/hw/chipset/i440fx.rs +++ b/lib/propolis/src/hw/chipset/i440fx.rs @@ -21,6 +21,7 @@ use crate::hw::pci::{ self, Bdf, INTxPinID, LintrCfg, PcieCfgDecoder, PioCfgDecoder, }; use crate::intr_pins::{IntrPin, LegacyPIC, LegacyPin, NoOpPin}; +use crate::lifecycle; use crate::migrate::*; use crate::mmio::MmioFn; use crate::pio::{PioBus, PioFn}; @@ -155,6 +156,7 @@ pub struct Opts { pub struct I440FxHostBridge { pci_state: pci::DeviceState, + indicator: lifecycle::Indicator, pci_topology: Arc, pci_cfg: PioCfgDecoder, @@ -189,6 +191,7 @@ impl I440FxHostBridge { Arc::new(Self { pci_state, + indicator: Default::default(), pci_topology, pci_cfg, @@ -276,6 +279,19 @@ impl Lifecycle for I440FxHostBridge { fn reset(&self) { self.pci_state.reset(self); } + fn start(&self) -> anyhow::Result<()> { + self.indicator.start(); + Ok(()) + } + fn pause(&self) { + self.indicator.pause(); + } + fn resume(&self) { + self.indicator.resume(); + } + fn halt(&self) { + self.indicator.halt(); + } fn migrate(&self) -> Migrator<'_> { Migrator::Multi(self) } diff --git a/lib/propolis/src/lifecycle.rs b/lib/propolis/src/lifecycle.rs index 1d8e194c5..c138ae7c0 100644 --- a/lib/propolis/src/lifecycle.rs +++ b/lib/propolis/src/lifecycle.rs @@ -173,7 +173,7 @@ impl IndicatedState { const fn valid_transition(old: Self, new: Self) -> bool { use IndicatedState::*; match (old, new) { - (Init, Run) | (Init, Halt) => true, + (Init, Run) | (Init, Pause) => true, (Run, Pause) => true, (Pause, Run) | (Pause, Halt) => true, _ => false, diff --git a/phd-tests/tests/src/server_state_machine.rs b/phd-tests/tests/src/server_state_machine.rs index c15a59641..352019751 100644 --- a/phd-tests/tests/src/server_state_machine.rs +++ b/phd-tests/tests/src/server_state_machine.rs @@ -29,6 +29,22 @@ async fn instance_start_stop_test(ctx: &Framework) { .await?; } +#[phd_testcase] +async fn instance_stop_unstarted_test(ctx: &Framework) { + let mut vm = ctx.spawn_default_vm("instance_stop_unstarted_test").await?; + + vm.instance_ensure().await?; + let instance = vm.get().await?.instance; + assert_eq!(instance.state, InstanceState::Creating); + + // At this point the VM is created and its resources are held as + // appropriate. Stopping the VM will cause propolis-server to destroy the + // VM, releasing those resources and getting the server ready for shutdown. + vm.stop().await?; + vm.wait_for_state(InstanceState::Destroyed, Duration::from_secs(60)) + .await?; +} + #[phd_testcase] async fn instance_stop_causes_destroy_test(ctx: &Framework) { let mut vm =