mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2026-08-05 02:19:16 +00:00
vmm: support migration of paused VMs
This extends migration to also support paused VMs, preserving the paused state on the destination. Changes: - Add CompletePaused protocol command that finalizes migration without resuming the VM on the destination - Skip the pause step during migration if the VM is already paused - On migration failure, only restore the running state if the VM was originally running (not paused) Signed-off-by: Nguyen Dinh Phi <phind.uet@gmail.com>
This commit is contained in:
committed by
Rob Bradford
parent
e2b9fa261b
commit
23fc9ca258
@@ -986,6 +986,10 @@ impl Vmm {
|
||||
StateReceived {
|
||||
state_receive_begin,
|
||||
} => match req.command() {
|
||||
Command::CompletePaused => {
|
||||
debug!("Migration (incoming): Receiving final state of a paused VM");
|
||||
Ok(Completed)
|
||||
}
|
||||
Command::Complete => {
|
||||
// The unwrap is safe, because the state machine makes sure we called
|
||||
// vm_receive_state before, which creates the VM.
|
||||
@@ -1361,7 +1365,9 @@ impl Vmm {
|
||||
mem_send,
|
||||
)?;
|
||||
let downtime_begin = Instant::now();
|
||||
vm.pause()?;
|
||||
if vm.get_state() != VmState::Paused {
|
||||
vm.pause()?;
|
||||
}
|
||||
|
||||
// Send last batch of dirty pages: final iteration
|
||||
{
|
||||
@@ -1391,6 +1397,7 @@ impl Vmm {
|
||||
#[cfg(all(feature = "kvm", target_arch = "x86_64"))]
|
||||
hypervisor: &dyn hypervisor::Hypervisor,
|
||||
send_data_migration: &VmSendMigrationData,
|
||||
initial_vm_state: VmState,
|
||||
) -> result::Result<(), MigratableError> {
|
||||
// State machine that is updated with more context as we progress.
|
||||
let mut ctx = OngoingMigrationContext::new();
|
||||
@@ -1461,9 +1468,11 @@ impl Vmm {
|
||||
vm.start_migration()?;
|
||||
|
||||
if send_data_migration.local {
|
||||
// Now pause VM
|
||||
// Now pause VM (skip if already paused, e.g. migrating a paused VM)
|
||||
let downtime_begin = Instant::now();
|
||||
vm.pause()?;
|
||||
if vm.get_state() != VmState::Paused {
|
||||
vm.pause()?;
|
||||
}
|
||||
ctx.set_vm_paused(
|
||||
downtime_begin,
|
||||
// No memory was transferred
|
||||
@@ -1509,10 +1518,15 @@ impl Vmm {
|
||||
// When this returns, we know the VM was resumed (if it was running
|
||||
// before the migration) and that the receiving VMM acquired disk
|
||||
// locks again.
|
||||
let complete_req = if initial_vm_state == VmState::Running {
|
||||
Request::complete()
|
||||
} else {
|
||||
Request::complete_paused()
|
||||
};
|
||||
let (_, complete_duration) = measure_ok(|| {
|
||||
migration_transport::send_request_expect_ok(
|
||||
&mut socket,
|
||||
Request::complete(),
|
||||
complete_req,
|
||||
MigratableError::MigrateSend(anyhow!("Error completing migration")),
|
||||
)
|
||||
})?;
|
||||
@@ -2578,13 +2592,10 @@ impl RequestHandler for Vmm {
|
||||
.as_mut()
|
||||
.ok_or_else(|| MigratableError::MigrateSend(anyhow!("VM is not running")))?;
|
||||
|
||||
// Only running VMs can be migrated: Future work can fix this to allow
|
||||
// also the migration of paused VMs while preserving the state in success
|
||||
// and error case. See #7815.
|
||||
if vm.get_state() != VmState::Running {
|
||||
let initial_vm_state = vm.get_state();
|
||||
if initial_vm_state != VmState::Running && initial_vm_state != VmState::Paused {
|
||||
return Err(MigratableError::MigrateSend(anyhow!(
|
||||
"VM is not in running state: {:?}",
|
||||
vm.get_state()
|
||||
"VM is not running or paused: {initial_vm_state:?}"
|
||||
)));
|
||||
}
|
||||
|
||||
@@ -2594,6 +2605,7 @@ impl RequestHandler for Vmm {
|
||||
#[cfg(all(feature = "kvm", target_arch = "x86_64"))]
|
||||
self.hypervisor.as_ref(),
|
||||
&send_data_migration,
|
||||
initial_vm_state,
|
||||
)
|
||||
.map_err(|migration_err| {
|
||||
error!("Migration failed: {migration_err:?}");
|
||||
@@ -2606,7 +2618,10 @@ impl RequestHandler for Vmm {
|
||||
return e;
|
||||
}
|
||||
|
||||
if vm.get_state() == VmState::Paused
|
||||
// Only resume if the VM was originally running; a VM that was already
|
||||
// paused before migration should remain paused after failure.
|
||||
if initial_vm_state == VmState::Running
|
||||
&& vm.get_state() == VmState::Paused
|
||||
&& let Err(e) = vm.resume()
|
||||
{
|
||||
return e;
|
||||
|
||||
Reference in New Issue
Block a user