mirror of
				https://github.com/kata-containers/kata-containers.git
				synced 2025-10-31 09:26:52 +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:
		
							
								
								
									
										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. | /// Wrapper over the microVM general information. | ||||||
| mod instance_info; | mod instance_info; | ||||||
| pub use self::instance_info::{InstanceInfo, InstanceState}; | 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::fs::File; | ||||||
| use std::sync::mpsc::{Receiver, Sender, TryRecvError}; | 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 vmm_sys_util::eventfd::EventFd; | ||||||
|  |  | ||||||
| use crate::error::{Result, StartMicrovmError, StopMicrovmError}; | use crate::error::{Result, StartMicrovmError, StopMicrovmError}; | ||||||
| use crate::event_manager::EventManager; | use crate::event_manager::EventManager; | ||||||
| use crate::vm::{KernelConfigInfo, VmConfigInfo}; | use crate::vm::{CpuTopology, KernelConfigInfo, VmConfigInfo}; | ||||||
| use crate::vmm::Vmm; | use crate::vmm::Vmm; | ||||||
|  |  | ||||||
| use super::*; | use super::*; | ||||||
| @@ -38,6 +38,11 @@ pub enum VmmActionError { | |||||||
|     /// The action `StopMicroVm` failed either because of bad user input or an internal error. |     /// The action `StopMicroVm` failed either because of bad user input or an internal error. | ||||||
|     #[error("failed to shutdown the VM: {0}")] |     #[error("failed to shutdown the VM: {0}")] | ||||||
|     StopMicrovm(#[source] StopMicrovmError), |     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 | /// 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 |     /// 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. |     /// shutdown the vcpu threads and destory all of the object. | ||||||
|     ShutdownMicroVm, |     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 | /// 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 { | pub enum VmmData { | ||||||
|     /// No data is sent on the channel. |     /// No data is sent on the channel. | ||||||
|     Empty, |     Empty, | ||||||
|  |     /// The microVM configuration represented by `VmConfigInfo`. | ||||||
|  |     MachineConfiguration(Box<VmConfigInfo>), | ||||||
| } | } | ||||||
|  |  | ||||||
| /// Request data type used to communicate between the API and the VMM. | /// 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::StartMicroVm => self.start_microvm(vmm, event_mgr), | ||||||
|             VmmAction::ShutdownMicroVm => self.shutdown_microvm(vmm), |             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); |         debug!("send vmm response: {:?}", response); | ||||||
| @@ -193,4 +213,134 @@ impl VmmService { | |||||||
|  |  | ||||||
|         Ok(VmmData::Empty) |         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,7 +689,10 @@ impl Vm { | |||||||
|             .state = InstanceState::Starting; |             .state = InstanceState::Starting; | ||||||
|  |  | ||||||
|         self.init_guest_memory()?; |         self.init_guest_memory()?; | ||||||
|         let vm_as = self.vm_as().cloned().ok_or(StartMicrovmError::AddressManagerError( |         let vm_as = self | ||||||
|  |             .vm_as() | ||||||
|  |             .cloned() | ||||||
|  |             .ok_or(StartMicrovmError::AddressManagerError( | ||||||
|                 AddressManagerError::GuestMemoryNotInitialized, |                 AddressManagerError::GuestMemoryNotInitialized, | ||||||
|             ))?; |             ))?; | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user