Refactor VM config to support different CPU topologies
This is preliminary work to support crosvm's --host-cpu-topology (WIP),
which will make it possible to mirror host's CPU topology in the guest.
As a first step, we refactor AVF's system API to stop accepting number
of vCPUs as an argument, but instead only expose two topology configs:
1 vCPU (default) and matching the host's CPU topology.
For the time being, the latter results in crosvm started with `--cpu
<nproc>`.
Bug: 266664564
Test: atest -p packages/modules/Virtualization:avf-presubmit
Change-Id: I03a37be0b68b93dc0fa6e84fd51ca3bdefbe6dde
diff --git a/virtualizationmanager/src/aidl.rs b/virtualizationmanager/src/aidl.rs
index 678c91f..89f74d6 100644
--- a/virtualizationmanager/src/aidl.rs
+++ b/virtualizationmanager/src/aidl.rs
@@ -27,6 +27,7 @@
ErrorCode::ErrorCode,
};
use android_system_virtualizationservice::aidl::android::system::virtualizationservice::{
+ CpuTopology::CpuTopology,
DiskImage::DiskImage,
IVirtualMachine::{BnVirtualMachine, IVirtualMachine},
IVirtualMachineCallback::IVirtualMachineCallback,
@@ -384,6 +385,18 @@
})
.collect::<Result<Vec<DiskFile>, _>>()?;
+ let (cpus, host_cpu_topology) = match config.cpuTopology {
+ CpuTopology::MATCH_HOST => (None, true),
+ CpuTopology::ONE_CPU => (NonZeroU32::new(1), false),
+ val => {
+ error!("Unexpected value of CPU topology: {:?}", val);
+ return Err(Status::new_service_specific_error_str(
+ -1,
+ Some(format!("Failed to parse CPU topology value: {:?}", val)),
+ ));
+ }
+ };
+
// Creating this ramdump file unconditionally is not harmful as ramdump will be created
// only when the VM is configured as such. `ramdump_write` is sent to crosvm and will
// be the backing store for the /dev/hvc1 where VM will emit ramdump to. `ramdump_read`
@@ -408,7 +421,8 @@
params: config.params.to_owned(),
protected: *is_protected,
memory_mib: config.memoryMib.try_into().ok().and_then(NonZeroU32::new),
- cpus: config.numCpus.try_into().ok().and_then(NonZeroU32::new),
+ cpus,
+ host_cpu_topology,
task_profiles: config.taskProfiles.clone(),
console_fd,
log_fd,
@@ -567,7 +581,7 @@
vm_config.name = config.name.clone();
vm_config.protectedVm = config.protectedVm;
- vm_config.numCpus = config.numCpus;
+ vm_config.cpuTopology = config.cpuTopology;
vm_config.taskProfiles = config.taskProfiles.clone();
// Microdroid takes additional init ramdisk & (optionally) storage image
diff --git a/virtualizationmanager/src/atom.rs b/virtualizationmanager/src/atom.rs
index c33f262..567fce9 100644
--- a/virtualizationmanager/src/atom.rs
+++ b/virtualizationmanager/src/atom.rs
@@ -19,6 +19,7 @@
use crate::get_calling_uid;
use android_system_virtualizationcommon::aidl::android::system::virtualizationcommon::DeathReason::DeathReason;
use android_system_virtualizationservice::aidl::android::system::virtualizationservice::{
+ CpuTopology::CpuTopology,
IVirtualMachine::IVirtualMachine,
VirtualMachineAppConfig::{Payload::Payload, VirtualMachineAppConfig},
VirtualMachineConfig::VirtualMachineConfig,
@@ -38,6 +39,8 @@
use std::time::{Duration, SystemTime};
use zip::ZipArchive;
+const INVALID_NUM_CPUS: i32 = -1;
+
fn get_apex_list(config: &VirtualMachineAppConfig) -> String {
match &config.payload {
Payload::PayloadConfig(_) => String::new(),
@@ -76,6 +79,19 @@
}
}
+// Returns the number of CPUs configured in the host system.
+// This matches how crosvm determines the number of logical cores.
+// For telemetry purposes only.
+pub(crate) fn get_num_cpus() -> Option<usize> {
+ // SAFETY - Only integer constants passed back and forth.
+ let ret = unsafe { libc::sysconf(libc::_SC_NPROCESSORS_CONF) };
+ if ret > 0 {
+ ret.try_into().ok()
+ } else {
+ None
+ }
+}
+
/// Write the stats of VMCreation to statsd
pub fn write_vm_creation_stats(
config: &VirtualMachineConfig,
@@ -94,23 +110,33 @@
binder_exception_code = e.exception_code() as i32;
}
}
- let (vm_identifier, config_type, num_cpus, memory_mib, apexes) = match config {
+ let (vm_identifier, config_type, cpu_topology, memory_mib, apexes) = match config {
VirtualMachineConfig::AppConfig(config) => (
config.name.clone(),
vm_creation_requested::ConfigType::VirtualMachineAppConfig,
- config.numCpus,
+ config.cpuTopology,
config.memoryMib,
get_apex_list(config),
),
VirtualMachineConfig::RawConfig(config) => (
config.name.clone(),
vm_creation_requested::ConfigType::VirtualMachineRawConfig,
- config.numCpus,
+ config.cpuTopology,
config.memoryMib,
String::new(),
),
};
+ let num_cpus: i32 = match cpu_topology {
+ CpuTopology::MATCH_HOST => {
+ get_num_cpus().and_then(|v| v.try_into().ok()).unwrap_or_else(|| {
+ warn!("Failed to determine the number of CPUs in the host");
+ INVALID_NUM_CPUS
+ })
+ }
+ _ => 1,
+ };
+
let atom = AtomVmCreationRequested {
uid: get_calling_uid() as i32,
vmIdentifier: vm_identifier,
diff --git a/virtualizationmanager/src/crosvm.rs b/virtualizationmanager/src/crosvm.rs
index 19d862a..ea1146e 100644
--- a/virtualizationmanager/src/crosvm.rs
+++ b/virtualizationmanager/src/crosvm.rs
@@ -15,7 +15,7 @@
//! Functions for running instances of `crosvm`.
use crate::aidl::{remove_temporary_files, Cid, VirtualMachineCallbacks};
-use crate::atom::write_vm_exited_stats;
+use crate::atom::{get_num_cpus, write_vm_exited_stats};
use anyhow::{anyhow, bail, Context, Error, Result};
use command_fds::CommandFdExt;
use lazy_static::lazy_static;
@@ -97,6 +97,7 @@
pub protected: bool,
pub memory_mib: Option<NonZeroU32>,
pub cpus: Option<NonZeroU32>,
+ pub host_cpu_topology: bool,
pub task_profiles: Vec<String>,
pub console_fd: Option<File>,
pub log_fd: Option<File>,
@@ -732,6 +733,15 @@
command.arg("--cpus").arg(cpus.to_string());
}
+ if config.host_cpu_topology {
+ // TODO(b/266664564): replace with --host-cpu-topology once available
+ if let Some(cpus) = get_num_cpus() {
+ command.arg("--cpus").arg(cpus.to_string());
+ } else {
+ bail!("Could not determine the number of CPUs in the system");
+ }
+ }
+
if !config.task_profiles.is_empty() {
command.arg("--task-profiles").arg(config.task_profiles.join(","));
}