mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2024-08-23 00:11:16 +00:00
vmm: vm: Verify that state transitions are valid
We should return an explicit error when the transition from on VM state to another is invalid. The valid_transition() routine for the VmState enum essentially describes the VM state machine. Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
This commit is contained in:
parent
ef090cf37d
commit
097b30669f
@ -218,6 +218,9 @@ pub enum Error {
|
|||||||
|
|
||||||
/// Cannot clone EventFd.
|
/// Cannot clone EventFd.
|
||||||
EventFdClone(io::Error),
|
EventFdClone(io::Error),
|
||||||
|
|
||||||
|
/// Invalid VM state transition
|
||||||
|
InvalidStateTransition(VmState, VmState),
|
||||||
}
|
}
|
||||||
pub type Result<T> = result::Result<T, Error>;
|
pub type Result<T> = result::Result<T, Error>;
|
||||||
|
|
||||||
@ -436,7 +439,7 @@ pub struct VmInfo<'a> {
|
|||||||
pub vm_cfg: &'a VmConfig,
|
pub vm_cfg: &'a VmConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
#[derive(Clone, Copy, Debug, Deserialize, Serialize, PartialEq)]
|
||||||
pub enum VmState {
|
pub enum VmState {
|
||||||
Created,
|
Created,
|
||||||
Running,
|
Running,
|
||||||
@ -444,6 +447,40 @@ pub enum VmState {
|
|||||||
Paused,
|
Paused,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl VmState {
|
||||||
|
fn valid_transition(self, new_state: VmState) -> Result<()> {
|
||||||
|
match self {
|
||||||
|
VmState::Created => match new_state {
|
||||||
|
VmState::Created | VmState::Shutdown | VmState::Paused => {
|
||||||
|
Err(Error::InvalidStateTransition(self, new_state))
|
||||||
|
}
|
||||||
|
VmState::Running => Ok(()),
|
||||||
|
},
|
||||||
|
|
||||||
|
VmState::Running => match new_state {
|
||||||
|
VmState::Created | VmState::Running => {
|
||||||
|
Err(Error::InvalidStateTransition(self, new_state))
|
||||||
|
}
|
||||||
|
VmState::Paused | VmState::Shutdown => Ok(()),
|
||||||
|
},
|
||||||
|
|
||||||
|
VmState::Shutdown => match new_state {
|
||||||
|
VmState::Paused | VmState::Created | VmState::Shutdown => {
|
||||||
|
Err(Error::InvalidStateTransition(self, new_state))
|
||||||
|
}
|
||||||
|
VmState::Running => Ok(()),
|
||||||
|
},
|
||||||
|
|
||||||
|
VmState::Paused => match new_state {
|
||||||
|
VmState::Created | VmState::Paused => {
|
||||||
|
Err(Error::InvalidStateTransition(self, new_state))
|
||||||
|
}
|
||||||
|
VmState::Running | VmState::Shutdown => Ok(()),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Vm {
|
pub struct Vm {
|
||||||
fd: Arc<VmFd>,
|
fd: Arc<VmFd>,
|
||||||
kernel: File,
|
kernel: File,
|
||||||
@ -773,6 +810,11 @@ impl Vm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn shutdown(&mut self) -> Result<()> {
|
pub fn shutdown(&mut self) -> Result<()> {
|
||||||
|
let mut state = self.state.try_write().map_err(|_| Error::PoisonedState)?;
|
||||||
|
let new_state = VmState::Shutdown;
|
||||||
|
|
||||||
|
state.valid_transition(new_state)?;
|
||||||
|
|
||||||
if self.on_tty {
|
if self.on_tty {
|
||||||
// Don't forget to set the terminal in canonical mode
|
// Don't forget to set the terminal in canonical mode
|
||||||
// before to exit.
|
// before to exit.
|
||||||
@ -805,13 +847,17 @@ impl Vm {
|
|||||||
thread.join().map_err(|_| Error::ThreadCleanup)?
|
thread.join().map_err(|_| Error::ThreadCleanup)?
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut state = self.state.try_write().map_err(|_| Error::PoisonedState)?;
|
*state = new_state;
|
||||||
*state = VmState::Shutdown;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pause(&mut self) -> Result<()> {
|
pub fn pause(&mut self) -> Result<()> {
|
||||||
|
let mut state = self.state.try_write().map_err(|_| Error::PoisonedState)?;
|
||||||
|
let new_state = VmState::Paused;
|
||||||
|
|
||||||
|
state.valid_transition(new_state)?;
|
||||||
|
|
||||||
// Tell the vCPUs to pause themselves next time they exit
|
// Tell the vCPUs to pause themselves next time they exit
|
||||||
self.vcpus_pause_signalled.store(true, Ordering::SeqCst);
|
self.vcpus_pause_signalled.store(true, Ordering::SeqCst);
|
||||||
|
|
||||||
@ -825,13 +871,17 @@ impl Vm {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut state = self.state.try_write().map_err(|_| Error::PoisonedState)?;
|
*state = new_state;
|
||||||
*state = VmState::Paused;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn resume(&mut self) -> Result<()> {
|
pub fn resume(&mut self) -> Result<()> {
|
||||||
|
let mut state = self.state.try_write().map_err(|_| Error::PoisonedState)?;
|
||||||
|
let new_state = VmState::Running;
|
||||||
|
|
||||||
|
state.valid_transition(new_state)?;
|
||||||
|
|
||||||
// Toggle the vCPUs pause boolean
|
// Toggle the vCPUs pause boolean
|
||||||
self.vcpus_pause_signalled.store(false, Ordering::SeqCst);
|
self.vcpus_pause_signalled.store(false, Ordering::SeqCst);
|
||||||
|
|
||||||
@ -844,8 +894,7 @@ impl Vm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// And we're back to the Running state.
|
// And we're back to the Running state.
|
||||||
let mut state = self.state.try_write().map_err(|_| Error::PoisonedState)?;
|
*state = new_state;
|
||||||
*state = VmState::Running;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -860,6 +909,14 @@ impl Vm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn boot(&mut self) -> Result<()> {
|
pub fn boot(&mut self) -> Result<()> {
|
||||||
|
let current_state = self.get_state()?;
|
||||||
|
if current_state == VmState::Paused {
|
||||||
|
return self.resume();
|
||||||
|
}
|
||||||
|
|
||||||
|
let new_state = VmState::Running;
|
||||||
|
current_state.valid_transition(new_state)?;
|
||||||
|
|
||||||
let entry_addr = self.load_kernel()?;
|
let entry_addr = self.load_kernel()?;
|
||||||
let vcpu_count = u8::from(&self.config.cpus);
|
let vcpu_count = u8::from(&self.config.cpus);
|
||||||
let vcpu_thread_barrier = Arc::new(Barrier::new((vcpu_count + 1) as usize));
|
let vcpu_thread_barrier = Arc::new(Barrier::new((vcpu_count + 1) as usize));
|
||||||
@ -965,7 +1022,7 @@ impl Vm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mut state = self.state.try_write().map_err(|_| Error::PoisonedState)?;
|
let mut state = self.state.try_write().map_err(|_| Error::PoisonedState)?;
|
||||||
*state = VmState::Running;
|
*state = new_state;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -1002,7 +1059,7 @@ impl Vm {
|
|||||||
self.state
|
self.state
|
||||||
.try_read()
|
.try_read()
|
||||||
.map_err(|_| Error::PoisonedState)
|
.map_err(|_| Error::PoisonedState)
|
||||||
.map(|state| state.clone())
|
.map(|state| *state)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user