mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2026-08-05 02:19:16 +00:00
vmm: Implement VM pause
In order to pause a VM, we signal all the vCPU threads to get them out of vmx non-root. Once out, the vCPU thread will check for a an atomic pause boolean. If it's set to true, then the thread will park until being resumed. Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
This commit is contained in:
committed by
Sebastien Boeuf
parent
80c3fd922a
commit
4ac0cb9cff
@@ -441,6 +441,7 @@ pub enum VmState {
|
||||
Created,
|
||||
Booted,
|
||||
Shutdown,
|
||||
Paused,
|
||||
}
|
||||
|
||||
pub struct Vm {
|
||||
@@ -454,6 +455,7 @@ pub struct Vm {
|
||||
on_tty: bool,
|
||||
creation_ts: std::time::Instant,
|
||||
vcpus_kill_signalled: Arc<AtomicBool>,
|
||||
vcpus_pause_signalled: Arc<AtomicBool>,
|
||||
// Reboot (reset) control
|
||||
reset_evt: EventFd,
|
||||
signals: Option<Signals>,
|
||||
@@ -685,6 +687,7 @@ impl Vm {
|
||||
on_tty,
|
||||
creation_ts,
|
||||
vcpus_kill_signalled: Arc::new(AtomicBool::new(false)),
|
||||
vcpus_pause_signalled: Arc::new(AtomicBool::new(false)),
|
||||
reset_evt,
|
||||
signals: None,
|
||||
state: RwLock::new(VmState::Created),
|
||||
@@ -808,6 +811,26 @@ impl Vm {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn pause(&mut self) -> Result<()> {
|
||||
// Tell the vCPUs to pause themselves next time they exit
|
||||
self.vcpus_pause_signalled.store(true, Ordering::SeqCst);
|
||||
|
||||
// Signal to the spawned threads (vCPUs and console signal handler). For the vCPU threads
|
||||
// this will interrupt the KVM_RUN ioctl() allowing the loop to check the boolean set
|
||||
// above. The signal handler thread will ignore this signal
|
||||
for thread in self.threads.iter() {
|
||||
let signum = validate_signal_num(VCPU_RTSIG_OFFSET, true).unwrap();
|
||||
unsafe {
|
||||
libc::pthread_kill(thread.as_pthread_t(), signum);
|
||||
}
|
||||
}
|
||||
|
||||
let mut state = self.state.try_write().map_err(|_| Error::PoisonedState)?;
|
||||
*state = VmState::Paused;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn os_signal_handler(signals: Signals, console_input_clone: Arc<Console>) {
|
||||
for signal in signals.forever() {
|
||||
if signal == SIGWINCH {
|
||||
@@ -838,6 +861,7 @@ impl Vm {
|
||||
|
||||
let reset_evt = self.reset_evt.try_clone().unwrap();
|
||||
let vcpu_kill_signalled = self.vcpus_kill_signalled.clone();
|
||||
let vcpu_pause_signalled = self.vcpus_pause_signalled.clone();
|
||||
self.threads.push(
|
||||
thread::Builder::new()
|
||||
.name(format!("vcpu{}", vcpu.id))
|
||||
@@ -876,6 +900,17 @@ impl Vm {
|
||||
if vcpu_kill_signalled.load(Ordering::SeqCst) {
|
||||
break;
|
||||
}
|
||||
|
||||
// If we are being told to pause, we park the thread
|
||||
// until the pause boolean is toggled.
|
||||
// The resume operation is responsible for toggling
|
||||
// the boolean and unpark the thread.
|
||||
// We enter a loop because park() could spuriously
|
||||
// return. We will then park() again unless the
|
||||
// pause boolean has been toggled.
|
||||
while vcpu_pause_signalled.load(Ordering::SeqCst) {
|
||||
thread::park();
|
||||
}
|
||||
}
|
||||
})
|
||||
.map_err(Error::VcpuSpawn)?,
|
||||
|
||||
Reference in New Issue
Block a user