mirror of
https://github.com/kata-containers/kata-containers.git
synced 2025-04-28 19:54:35 +00:00
dragonball: add set_vm_configuration api
Set virtual machine configuration configurations. Signed-off-by: wllenyj <wllenyj@linux.alibaba.com>
This commit is contained in:
parent
95fa0c70c3
commit
89b9ba8603
86
src/dragonball/src/api/v1/machine_config.rs
Normal file
86
src/dragonball/src/api/v1/machine_config.rs
Normal file
@ -0,0 +1,86 @@
|
||||
// Copyright (C) 2022 Alibaba Cloud. All rights reserved.
|
||||
// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
/// We only support this number of vcpus for now. Mostly because we have set all vcpu related metrics as u8
|
||||
/// and breaking u8 will take extra efforts.
|
||||
pub const MAX_SUPPORTED_VCPUS: u8 = 254;
|
||||
|
||||
/// Memory hotplug value should have alignment in this size (unit: MiB)
|
||||
pub const MEMORY_HOTPLUG_ALIGHMENT: u8 = 64;
|
||||
|
||||
/// Errors associated with configuring the microVM.
|
||||
#[derive(Debug, PartialEq, thiserror::Error)]
|
||||
pub enum VmConfigError {
|
||||
/// Cannot update the configuration of the microvm post boot.
|
||||
#[error("update operation is not allowed after boot")]
|
||||
UpdateNotAllowedPostBoot,
|
||||
|
||||
/// The max vcpu count is invalid.
|
||||
#[error("the vCPU number shouldn't large than {}", MAX_SUPPORTED_VCPUS)]
|
||||
VcpuCountExceedsMaximum,
|
||||
|
||||
/// The vcpu count is invalid. When hyperthreading is enabled, the `cpu_count` must be either
|
||||
/// 1 or an even number.
|
||||
#[error(
|
||||
"the vCPU number '{0}' can only be 1 or an even number when hyperthreading is enabled"
|
||||
)]
|
||||
InvalidVcpuCount(u8),
|
||||
|
||||
/// The threads_per_core is invalid. It should be either 1 or 2.
|
||||
#[error("the threads_per_core number '{0}' can only be 1 or 2")]
|
||||
InvalidThreadsPerCore(u8),
|
||||
|
||||
/// The cores_per_die is invalid. It should be larger than 0.
|
||||
#[error("the cores_per_die number '{0}' can only be larger than 0")]
|
||||
InvalidCoresPerDie(u8),
|
||||
|
||||
/// The dies_per_socket is invalid. It should be larger than 0.
|
||||
#[error("the dies_per_socket number '{0}' can only be larger than 0")]
|
||||
InvalidDiesPerSocket(u8),
|
||||
|
||||
/// The socket number is invalid. It should be either 1 or 2.
|
||||
#[error("the socket number '{0}' can only be 1 or 2")]
|
||||
InvalidSocket(u8),
|
||||
|
||||
/// max vcpu count inferred from cpu topology(threads_per_core * cores_per_die * dies_per_socket * sockets) should be larger or equal to vcpu_count
|
||||
#[error("the max vcpu count inferred from cpu topology '{0}' (threads_per_core * cores_per_die * dies_per_socket * sockets) should be larger or equal to vcpu_count")]
|
||||
InvalidCpuTopology(u8),
|
||||
|
||||
/// The max vcpu count is invalid.
|
||||
#[error(
|
||||
"the max vCPU number '{0}' shouldn't less than vCPU count and can only be 1 or an even number when hyperthreading is enabled"
|
||||
)]
|
||||
InvalidMaxVcpuCount(u8),
|
||||
|
||||
/// The memory size is invalid. The memory can only be an unsigned integer.
|
||||
#[error("the memory size 0x{0:x}MiB is invalid")]
|
||||
InvalidMemorySize(usize),
|
||||
|
||||
/// The hotplug memory size is invalid. The memory can only be an unsigned integer.
|
||||
#[error(
|
||||
"the hotplug memory size '{0}' (MiB) is invalid, must be multiple of {}",
|
||||
MEMORY_HOTPLUG_ALIGHMENT
|
||||
)]
|
||||
InvalidHotplugMemorySize(usize),
|
||||
|
||||
/// The memory type is invalid.
|
||||
#[error("the memory type '{0}' is invalid")]
|
||||
InvalidMemType(String),
|
||||
|
||||
/// The memory file path is invalid.
|
||||
#[error("the memory file path is invalid")]
|
||||
InvalidMemFilePath(String),
|
||||
|
||||
/// NUMA region memory size is invalid
|
||||
#[error("Total size of memory in NUMA regions: {0}, should matches memory size in config")]
|
||||
InvalidNumaRegionMemorySize(usize),
|
||||
|
||||
/// NUMA region vCPU count is invalid
|
||||
#[error("Total counts of vCPUs in NUMA regions: {0}, should matches max vcpu count in config")]
|
||||
InvalidNumaRegionCpuCount(u16),
|
||||
|
||||
/// NUMA region vCPU count is invalid
|
||||
#[error("Max id of vCPUs in NUMA regions: {0}, should matches max vcpu count in config")]
|
||||
InvalidNumaRegionCpuMaxId(u16),
|
||||
}
|
@ -15,3 +15,7 @@ pub use self::boot_source::{BootSourceConfig, BootSourceConfigError, DEFAULT_KER
|
||||
/// Wrapper over the microVM general information.
|
||||
mod instance_info;
|
||||
pub use self::instance_info::{InstanceInfo, InstanceState};
|
||||
|
||||
/// Wrapper for configuring the memory and CPU of the microVM.
|
||||
mod machine_config;
|
||||
pub use self::machine_config::{VmConfigError, MAX_SUPPORTED_VCPUS};
|
||||
|
@ -9,12 +9,12 @@
|
||||
use std::fs::File;
|
||||
use std::sync::mpsc::{Receiver, Sender, TryRecvError};
|
||||
|
||||
use log::{debug, error, warn};
|
||||
use log::{debug, error, info, warn};
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
|
||||
use crate::error::{Result, StartMicrovmError, StopMicrovmError};
|
||||
use crate::event_manager::EventManager;
|
||||
use crate::vm::{KernelConfigInfo, VmConfigInfo};
|
||||
use crate::vm::{CpuTopology, KernelConfigInfo, VmConfigInfo};
|
||||
use crate::vmm::Vmm;
|
||||
|
||||
use super::*;
|
||||
@ -38,6 +38,11 @@ pub enum VmmActionError {
|
||||
/// The action `StopMicroVm` failed either because of bad user input or an internal error.
|
||||
#[error("failed to shutdown the VM: {0}")]
|
||||
StopMicrovm(#[source] StopMicrovmError),
|
||||
|
||||
/// One of the actions `GetVmConfiguration` or `SetVmConfiguration` failed either because of bad
|
||||
/// input or an internal error.
|
||||
#[error("failed to set configuration for the VM: {0}")]
|
||||
MachineConfig(#[source] VmConfigError),
|
||||
}
|
||||
|
||||
/// This enum represents the public interface of the VMM. Each action contains various
|
||||
@ -55,6 +60,13 @@ pub enum VmmAction {
|
||||
/// When vmm is used as the crate by the other process, which is need to
|
||||
/// shutdown the vcpu threads and destory all of the object.
|
||||
ShutdownMicroVm,
|
||||
|
||||
/// Get the configuration of the microVM.
|
||||
GetVmConfiguration,
|
||||
|
||||
/// Set the microVM configuration (memory & vcpu) using `VmConfig` as input. This
|
||||
/// action can only be called before the microVM has booted.
|
||||
SetVmConfiguration(VmConfigInfo),
|
||||
}
|
||||
|
||||
/// The enum represents the response sent by the VMM in case of success. The response is either
|
||||
@ -63,6 +75,8 @@ pub enum VmmAction {
|
||||
pub enum VmmData {
|
||||
/// No data is sent on the channel.
|
||||
Empty,
|
||||
/// The microVM configuration represented by `VmConfigInfo`.
|
||||
MachineConfiguration(Box<VmConfigInfo>),
|
||||
}
|
||||
|
||||
/// Request data type used to communicate between the API and the VMM.
|
||||
@ -114,6 +128,12 @@ impl VmmService {
|
||||
}
|
||||
VmmAction::StartMicroVm => self.start_microvm(vmm, event_mgr),
|
||||
VmmAction::ShutdownMicroVm => self.shutdown_microvm(vmm),
|
||||
VmmAction::GetVmConfiguration => Ok(VmmData::MachineConfiguration(Box::new(
|
||||
self.machine_config.clone(),
|
||||
))),
|
||||
VmmAction::SetVmConfiguration(machine_config) => {
|
||||
self.set_vm_configuration(vmm, machine_config)
|
||||
}
|
||||
};
|
||||
|
||||
debug!("send vmm response: {:?}", response);
|
||||
@ -193,4 +213,134 @@ impl VmmService {
|
||||
|
||||
Ok(VmmData::Empty)
|
||||
}
|
||||
|
||||
/// Set virtual machine configuration configurations.
|
||||
pub fn set_vm_configuration(
|
||||
&mut self,
|
||||
vmm: &mut Vmm,
|
||||
machine_config: VmConfigInfo,
|
||||
) -> VmmRequestResult {
|
||||
use self::VmConfigError::*;
|
||||
use self::VmmActionError::MachineConfig;
|
||||
|
||||
let vm = vmm
|
||||
.get_vm_by_id_mut("")
|
||||
.ok_or(VmmActionError::InvalidVMID)?;
|
||||
if vm.is_vm_initialized() {
|
||||
return Err(MachineConfig(UpdateNotAllowedPostBoot));
|
||||
}
|
||||
|
||||
// If the check is successful, set it up together.
|
||||
let mut config = vm.vm_config().clone();
|
||||
if config.vcpu_count != machine_config.vcpu_count {
|
||||
let vcpu_count = machine_config.vcpu_count;
|
||||
// Check that the vcpu_count value is >=1.
|
||||
if vcpu_count == 0 {
|
||||
return Err(MachineConfig(InvalidVcpuCount(vcpu_count)));
|
||||
}
|
||||
config.vcpu_count = vcpu_count;
|
||||
}
|
||||
|
||||
if config.cpu_topology != machine_config.cpu_topology {
|
||||
let cpu_topology = &machine_config.cpu_topology;
|
||||
// Check if dies_per_socket, cores_per_die, threads_per_core and socket number is valid
|
||||
if cpu_topology.threads_per_core < 1 || cpu_topology.threads_per_core > 2 {
|
||||
return Err(MachineConfig(InvalidThreadsPerCore(
|
||||
cpu_topology.threads_per_core,
|
||||
)));
|
||||
}
|
||||
let vcpu_count_from_topo = cpu_topology
|
||||
.sockets
|
||||
.checked_mul(cpu_topology.dies_per_socket)
|
||||
.ok_or(MachineConfig(VcpuCountExceedsMaximum))?
|
||||
.checked_mul(cpu_topology.cores_per_die)
|
||||
.ok_or(MachineConfig(VcpuCountExceedsMaximum))?
|
||||
.checked_mul(cpu_topology.threads_per_core)
|
||||
.ok_or(MachineConfig(VcpuCountExceedsMaximum))?;
|
||||
if vcpu_count_from_topo > MAX_SUPPORTED_VCPUS {
|
||||
return Err(MachineConfig(VcpuCountExceedsMaximum));
|
||||
}
|
||||
if vcpu_count_from_topo < config.vcpu_count {
|
||||
return Err(MachineConfig(InvalidCpuTopology(vcpu_count_from_topo)));
|
||||
}
|
||||
config.cpu_topology = cpu_topology.clone();
|
||||
} else {
|
||||
// the same default
|
||||
let mut default_cpu_topology = CpuTopology {
|
||||
threads_per_core: 1,
|
||||
cores_per_die: config.vcpu_count,
|
||||
dies_per_socket: 1,
|
||||
sockets: 1,
|
||||
};
|
||||
if machine_config.max_vcpu_count > config.vcpu_count {
|
||||
default_cpu_topology.cores_per_die = machine_config.max_vcpu_count;
|
||||
}
|
||||
config.cpu_topology = default_cpu_topology;
|
||||
}
|
||||
let cpu_topology = &config.cpu_topology;
|
||||
let max_vcpu_from_topo = cpu_topology.threads_per_core
|
||||
* cpu_topology.cores_per_die
|
||||
* cpu_topology.dies_per_socket
|
||||
* cpu_topology.sockets;
|
||||
// If the max_vcpu_count inferred by cpu_topology is not equal to
|
||||
// max_vcpu_count, max_vcpu_count will be changed. currently, max vcpu size
|
||||
// is used when cpu_topology is not defined and help define the cores_per_die
|
||||
// for the default cpu topology.
|
||||
let mut max_vcpu_count = machine_config.max_vcpu_count;
|
||||
if max_vcpu_count < config.vcpu_count {
|
||||
return Err(MachineConfig(InvalidMaxVcpuCount(max_vcpu_count)));
|
||||
}
|
||||
if max_vcpu_from_topo != max_vcpu_count {
|
||||
max_vcpu_count = max_vcpu_from_topo;
|
||||
info!("Since max_vcpu_count is not equal to cpu topo information, we have changed the max vcpu count to {}", max_vcpu_from_topo);
|
||||
}
|
||||
config.max_vcpu_count = max_vcpu_count;
|
||||
|
||||
config.cpu_pm = machine_config.cpu_pm;
|
||||
config.mem_type = machine_config.mem_type;
|
||||
|
||||
let mem_size_mib_value = machine_config.mem_size_mib;
|
||||
// Support 1TB memory at most, 2MB aligned for huge page.
|
||||
if mem_size_mib_value == 0 || mem_size_mib_value > 0x10_0000 || mem_size_mib_value % 2 != 0
|
||||
{
|
||||
return Err(MachineConfig(InvalidMemorySize(mem_size_mib_value)));
|
||||
}
|
||||
config.mem_size_mib = mem_size_mib_value;
|
||||
|
||||
config.mem_file_path = machine_config.mem_file_path.clone();
|
||||
|
||||
let reserve_memory_bytes = machine_config.reserve_memory_bytes;
|
||||
// Reserved memory must be 2MB aligned and less than half of the total memory.
|
||||
if reserve_memory_bytes % 0x200000 != 0
|
||||
|| reserve_memory_bytes > (config.mem_size_mib as u64) << 20
|
||||
{
|
||||
return Err(MachineConfig(InvalidReservedMemorySize(
|
||||
reserve_memory_bytes as usize >> 20,
|
||||
)));
|
||||
}
|
||||
config.reserve_memory_bytes = reserve_memory_bytes;
|
||||
if config.mem_type == "hugetlbfs" && config.mem_file_path.is_empty() {
|
||||
return Err(MachineConfig(InvalidMemFilePath("".to_owned())));
|
||||
}
|
||||
config.vpmu_feature = machine_config.vpmu_feature;
|
||||
|
||||
let vm_id = vm.shared_info().read().unwrap().id.clone();
|
||||
let serial_path = match machine_config.serial_path {
|
||||
Some(value) => value,
|
||||
None => {
|
||||
if config.serial_path.is_none() {
|
||||
String::from("/run/dragonball/") + &vm_id + "_com1"
|
||||
} else {
|
||||
// Safe to unwrap() because we have checked it has a value.
|
||||
config.serial_path.as_ref().unwrap().clone()
|
||||
}
|
||||
}
|
||||
};
|
||||
config.serial_path = Some(serial_path);
|
||||
|
||||
vm.set_vm_config(config.clone());
|
||||
self.machine_config = config;
|
||||
|
||||
Ok(VmmData::Empty)
|
||||
}
|
||||
}
|
||||
|
@ -689,9 +689,12 @@ impl Vm {
|
||||
.state = InstanceState::Starting;
|
||||
|
||||
self.init_guest_memory()?;
|
||||
let vm_as = self.vm_as().cloned().ok_or(StartMicrovmError::AddressManagerError(
|
||||
AddressManagerError::GuestMemoryNotInitialized,
|
||||
))?;
|
||||
let vm_as = self
|
||||
.vm_as()
|
||||
.cloned()
|
||||
.ok_or(StartMicrovmError::AddressManagerError(
|
||||
AddressManagerError::GuestMemoryNotInitialized,
|
||||
))?;
|
||||
|
||||
self.init_vcpu_manager(vm_as.clone(), vcpu_seccomp_filter)
|
||||
.map_err(StartMicrovmError::Vcpu)?;
|
||||
|
Loading…
Reference in New Issue
Block a user