arch, vmm: Add CPUID check to the 'Config' step of live migration

We now send not only the 'VmConfig' at the 'Command::Config' step of
live migration, but also send the 'common CPUID'. In this way, we can
check the compatibility of CPUID features between the source and
destination VMs, and abort live migration early if needed.

Signed-off-by: Bo Chen <chen.bo@intel.com>
This commit is contained in:
Bo Chen
2021-07-16 11:33:03 -07:00
committed by Sebastien Boeuf
parent b4c2772031
commit 6d9c1eb638
3 changed files with 247 additions and 6 deletions
+83 -4
View File
@@ -284,6 +284,13 @@ pub fn start_vmm_thread(
Ok(thread)
}
#[derive(Clone, Deserialize, Serialize)]
struct VmMigrationConfig {
vm_config: Arc<Mutex<VmConfig>>,
#[cfg(all(feature = "kvm", target_arch = "x86_64"))]
common_cpuid: hypervisor::CpuId,
}
pub struct Vmm {
epoll: EpollContext,
exit_evt: EventFd,
@@ -743,8 +750,47 @@ impl Vmm {
socket
.read_exact(&mut data)
.map_err(MigratableError::MigrateSocket)?;
let config: VmConfig = serde_json::from_slice(&data).map_err(|e| {
MigratableError::MigrateReceive(anyhow!("Error deserialising config: {}", e))
let vm_migration_config: VmMigrationConfig =
serde_json::from_slice(&data).map_err(|e| {
MigratableError::MigrateReceive(anyhow!("Error deserialising config: {}", e))
})?;
#[cfg(all(feature = "kvm", target_arch = "x86_64"))]
// We check the `CPUID` compatibility of between the source vm and destination, which is
// mostly about feature compatibility and "topology/sgx" leaves are not relevant.
let dest_cpuid = {
let vm_config = &vm_migration_config.vm_config.lock().unwrap();
#[cfg(feature = "tdx")]
let tdx_enabled = vm_config.tdx.is_some();
let phys_bits = vm::physical_bits(
vm_config.cpus.max_phys_bits,
#[cfg(feature = "tdx")]
tdx_enabled,
);
arch::generate_common_cpuid(
self.hypervisor.clone(),
None,
None,
phys_bits,
vm_config.cpus.kvm_hyperv,
#[cfg(feature = "tdx")]
tdx_enabled,
)
.map_err(|e| {
MigratableError::MigrateReceive(anyhow!("Error generating common cpuid': {:?}", e))
})?
};
#[cfg(all(feature = "kvm", target_arch = "x86_64"))]
arch::CpuidFeatureEntry::check_cpuid_compatibility(
&vm_migration_config.common_cpuid,
&dest_cpuid,
)
.map_err(|e| {
MigratableError::MigrateReceive(anyhow!(
"Error checking cpu feature compatibility': {:?}",
e
))
})?;
let exit_evt = self.exit_evt.try_clone().map_err(|e| {
@@ -757,7 +803,7 @@ impl Vmm {
MigratableError::MigrateReceive(anyhow!("Error cloning activate EventFd: {}", e))
})?;
self.vm_config = Some(Arc::new(Mutex::new(config)));
self.vm_config = Some(vm_migration_config.vm_config);
let vm = Vm::new_from_migration(
self.vm_config.clone().unwrap(),
exit_evt,
@@ -998,7 +1044,40 @@ impl Vmm {
}
// Send config
let config_data = serde_json::to_vec(&vm.get_config()).unwrap();
let vm_config = vm.get_config();
#[cfg(all(feature = "kvm", target_arch = "x86_64"))]
let common_cpuid = {
#[cfg(feature = "tdx")]
let tdx_enabled = vm_config.lock().unwrap().tdx.is_some();
let phys_bits = vm::physical_bits(
vm_config.lock().unwrap().cpus.max_phys_bits,
#[cfg(feature = "tdx")]
tdx_enabled,
);
arch::generate_common_cpuid(
self.hypervisor.clone(),
None,
None,
phys_bits,
vm_config.lock().unwrap().cpus.kvm_hyperv,
#[cfg(feature = "tdx")]
tdx_enabled,
)
.map_err(|e| {
MigratableError::MigrateReceive(anyhow!(
"Error generating common cpuid': {:?}",
e
))
})?
};
let vm_migration_config = VmMigrationConfig {
vm_config,
#[cfg(all(feature = "kvm", target_arch = "x86_64"))]
common_cpuid,
};
let config_data = serde_json::to_vec(&vm_migration_config).unwrap();
Request::config(config_data.len() as u64).write_to(&mut socket)?;
socket
.write_all(&config_data)