@@ -670,7 +670,7 @@ impl RuntimeApiController {
670670 self . vmm . lock ( ) . expect ( "Poisoned lock" ) . version ( ) ,
671671 ) ) ,
672672 #[ cfg( target_arch = "x86_64" ) ]
673- HotplugRequest ( config ) => Ok ( VmmData :: Empty ) ,
673+ HotplugRequest ( request_type ) => self . handle_hotplug_request ( request_type ) ,
674674 PatchMMDS ( value) => self . patch_mmds ( value) ,
675675 Pause => self . pause ( ) ,
676676 PutMMDS ( value) => self . put_mmds ( value) ,
@@ -867,6 +867,31 @@ impl RuntimeApiController {
867867 . map_err ( NetworkInterfaceError :: DeviceUpdate )
868868 . map_err ( VmmActionError :: NetworkConfig )
869869 }
870+
871+ #[ cfg( target_arch = "x86_64" ) ]
872+ fn handle_hotplug_request (
873+ & mut self ,
874+ cfg : HotplugRequestConfig ,
875+ ) -> Result < VmmData , VmmActionError > {
876+ match cfg {
877+ HotplugRequestConfig :: Vcpu ( cfg) => {
878+ let result = self . vmm . lock ( ) . expect ( "Poisoned lock" ) . hotplug_vcpus ( cfg) ;
879+ result
880+ . map_err ( |err| VmmActionError :: HotplugRequest ( HotplugRequestError :: Vcpu ( err) ) )
881+ . and_then ( |machine_cfg_update| self . update_vm_config ( machine_cfg_update) )
882+ }
883+ }
884+ }
885+
886+ // Currently, this method is only used for vCPU hotplugging, which is not implemented for
887+ // aarch64, hence we must allow `dead_code`
888+ #[ allow( dead_code) ]
889+ fn update_vm_config ( & mut self , cfg : MachineConfigUpdate ) -> Result < VmmData , VmmActionError > {
890+ self . vm_resources
891+ . update_vm_config ( & cfg)
892+ . map ( |( ) | VmmData :: Empty )
893+ . map_err ( VmmActionError :: MachineConfig )
894+ }
870895}
871896
872897#[ cfg( test) ]
@@ -875,8 +900,14 @@ mod tests {
875900 use std:: path:: PathBuf ;
876901
877902 use seccompiler:: BpfThreadMap ;
903+ #[ cfg( target_arch = "x86_64" ) ]
904+ use vmm_config:: hotplug:: HotplugVcpuError ;
878905
879906 use super :: * ;
907+ // Currently, default_vmm only used for testing hotplugging, which is only implemented for
908+ // x86_64, so `unused_imports` must be allowed for aarch64 systems
909+ #[ cfg( target_arch = "x86_64" ) ]
910+ use crate :: builder:: tests:: default_vmm;
880911 use crate :: cpu_config:: templates:: test_utils:: build_test_template;
881912 use crate :: cpu_config:: templates:: { CpuTemplateType , StaticCpuTemplate } ;
882913 use crate :: devices:: virtio:: balloon:: { BalloonConfig , BalloonError } ;
@@ -885,6 +916,8 @@ mod tests {
885916 use crate :: devices:: virtio:: vsock:: VsockError ;
886917 use crate :: mmds:: data_store:: MmdsVersion ;
887918 use crate :: vmm_config:: balloon:: BalloonBuilder ;
919+ #[ cfg( target_arch = "x86_64" ) ]
920+ use crate :: vmm_config:: hotplug:: HotplugVcpuConfig ;
888921 use crate :: vmm_config:: machine_config:: VmConfig ;
889922 use crate :: vmm_config:: snapshot:: { MemBackendConfig , MemBackendType } ;
890923 use crate :: vmm_config:: vsock:: VsockBuilder ;
@@ -893,27 +926,55 @@ mod tests {
893926 impl PartialEq for VmmActionError {
894927 fn eq ( & self , other : & VmmActionError ) -> bool {
895928 use VmmActionError :: * ;
896- matches ! (
897- ( self , other) ,
898- ( BalloonConfig ( _) , BalloonConfig ( _) )
899- | ( BootSource ( _) , BootSource ( _) )
900- | ( CreateSnapshot ( _) , CreateSnapshot ( _) )
901- | ( DriveConfig ( _) , DriveConfig ( _) )
902- | ( InternalVmm ( _) , InternalVmm ( _) )
903- | ( LoadSnapshot ( _) , LoadSnapshot ( _) )
904- | ( MachineConfig ( _) , MachineConfig ( _) )
905- | ( Metrics ( _) , Metrics ( _) )
906- | ( Mmds ( _) , Mmds ( _) )
907- | ( MmdsLimitExceeded ( _) , MmdsLimitExceeded ( _) )
908- | ( MmdsConfig ( _) , MmdsConfig ( _) )
909- | ( NetworkConfig ( _) , NetworkConfig ( _) )
910- | ( NotSupported ( _) , NotSupported ( _) )
911- | ( OperationNotSupportedPostBoot , OperationNotSupportedPostBoot )
912- | ( OperationNotSupportedPreBoot , OperationNotSupportedPreBoot )
913- | ( StartMicrovm ( _) , StartMicrovm ( _) )
914- | ( VsockConfig ( _) , VsockConfig ( _) )
915- | ( EntropyDevice ( _) , EntropyDevice ( _) )
916- )
929+ #[ cfg( target_arch = "x86_64" ) ]
930+ {
931+ matches ! (
932+ ( self , other) ,
933+ ( BalloonConfig ( _) , BalloonConfig ( _) )
934+ | ( BootSource ( _) , BootSource ( _) )
935+ | ( CreateSnapshot ( _) , CreateSnapshot ( _) )
936+ | ( DriveConfig ( _) , DriveConfig ( _) )
937+ | ( HotplugRequest ( _) , HotplugRequest ( _) )
938+ | ( InternalVmm ( _) , InternalVmm ( _) )
939+ | ( LoadSnapshot ( _) , LoadSnapshot ( _) )
940+ | ( MachineConfig ( _) , MachineConfig ( _) )
941+ | ( Metrics ( _) , Metrics ( _) )
942+ | ( Mmds ( _) , Mmds ( _) )
943+ | ( MmdsLimitExceeded ( _) , MmdsLimitExceeded ( _) )
944+ | ( MmdsConfig ( _) , MmdsConfig ( _) )
945+ | ( NetworkConfig ( _) , NetworkConfig ( _) )
946+ | ( NotSupported ( _) , NotSupported ( _) )
947+ | ( OperationNotSupportedPostBoot , OperationNotSupportedPostBoot )
948+ | ( OperationNotSupportedPreBoot , OperationNotSupportedPreBoot )
949+ | ( StartMicrovm ( _) , StartMicrovm ( _) )
950+ | ( VsockConfig ( _) , VsockConfig ( _) )
951+ | ( EntropyDevice ( _) , EntropyDevice ( _) )
952+ )
953+ }
954+ #[ cfg( target_arch = "aarch64" ) ]
955+ {
956+ matches ! (
957+ ( self , other) ,
958+ ( BalloonConfig ( _) , BalloonConfig ( _) )
959+ | ( BootSource ( _) , BootSource ( _) )
960+ | ( CreateSnapshot ( _) , CreateSnapshot ( _) )
961+ | ( DriveConfig ( _) , DriveConfig ( _) )
962+ | ( InternalVmm ( _) , InternalVmm ( _) )
963+ | ( LoadSnapshot ( _) , LoadSnapshot ( _) )
964+ | ( MachineConfig ( _) , MachineConfig ( _) )
965+ | ( Metrics ( _) , Metrics ( _) )
966+ | ( Mmds ( _) , Mmds ( _) )
967+ | ( MmdsLimitExceeded ( _) , MmdsLimitExceeded ( _) )
968+ | ( MmdsConfig ( _) , MmdsConfig ( _) )
969+ | ( NetworkConfig ( _) , NetworkConfig ( _) )
970+ | ( NotSupported ( _) , NotSupported ( _) )
971+ | ( OperationNotSupportedPostBoot , OperationNotSupportedPostBoot )
972+ | ( OperationNotSupportedPreBoot , OperationNotSupportedPreBoot )
973+ | ( StartMicrovm ( _) , StartMicrovm ( _) )
974+ | ( VsockConfig ( _) , VsockConfig ( _) )
975+ | ( EntropyDevice ( _) , EntropyDevice ( _) )
976+ )
977+ }
917978 }
918979 }
919980
@@ -1106,6 +1167,8 @@ mod tests {
11061167 pub update_block_device_path_called : bool ,
11071168 pub update_block_device_vhost_user_config_called : bool ,
11081169 pub update_net_rate_limiters_called : bool ,
1170+ #[ cfg( target_arch = "x86_64" ) ]
1171+ pub hotplug_vcpus_called : bool ,
11091172 // when `true`, all self methods are forced to fail
11101173 pub force_errors : bool ,
11111174 }
@@ -1216,6 +1279,24 @@ mod tests {
12161279 Ok ( ( ) )
12171280 }
12181281
1282+ #[ cfg( target_arch = "x86_64" ) ]
1283+ pub fn hotplug_vcpus (
1284+ & mut self ,
1285+ _: HotplugVcpuConfig ,
1286+ ) -> Result < MachineConfigUpdate , HotplugVcpuError > {
1287+ if self . force_errors {
1288+ return Err ( HotplugVcpuError :: VcpuCountTooHigh ) ;
1289+ }
1290+ self . hotplug_vcpus_called = true ;
1291+ Ok ( MachineConfigUpdate {
1292+ vcpu_count : Some ( 1 ) ,
1293+ mem_size_mib : None ,
1294+ smt : None ,
1295+ cpu_template : None ,
1296+ track_dirty_pages : None ,
1297+ huge_pages : None ,
1298+ } )
1299+ }
12191300 pub fn instance_info ( & self ) -> InstanceInfo {
12201301 InstanceInfo :: default ( )
12211302 }
@@ -1830,6 +1911,12 @@ mod tests {
18301911 VmmAction :: SendCtrlAltDel ,
18311912 VmmActionError :: OperationNotSupportedPreBoot ,
18321913 ) ;
1914+
1915+ #[ cfg( target_arch = "x86_64" ) ]
1916+ check_preboot_request_err (
1917+ VmmAction :: HotplugRequest ( HotplugRequestConfig :: Vcpu ( HotplugVcpuConfig { add : 4 } ) ) ,
1918+ VmmActionError :: OperationNotSupportedPreBoot ,
1919+ ) ;
18331920 }
18341921
18351922 fn check_runtime_request < F > ( request : VmmAction , check_success : F )
@@ -2059,6 +2146,44 @@ mod tests {
20592146 ) ;
20602147 }
20612148
2149+ #[ test]
2150+ #[ cfg( target_arch = "x86_64" ) ]
2151+ fn test_runtime_hotplug_vcpu ( ) {
2152+ // Case 1. Valid input
2153+ let mut vmm = default_vmm ( ) ;
2154+ let config = HotplugVcpuConfig { add : 4 } ;
2155+ let result = vmm. hotplug_vcpus ( config) ;
2156+ assert_eq ! ( vmm. vcpus_handles. len( ) , 4 ) ;
2157+ result. unwrap ( ) ;
2158+
2159+ // Case 2. Vcpu count too low
2160+ let mut vmm = default_vmm ( ) ;
2161+ vmm. hotplug_vcpus ( HotplugVcpuConfig { add : 1 } ) . unwrap ( ) ;
2162+ assert_eq ! ( vmm. vcpus_handles. len( ) , 1 ) ;
2163+ let config = HotplugVcpuConfig { add : 0 } ;
2164+ let result = vmm. hotplug_vcpus ( config) ;
2165+ result. unwrap_err ( ) ;
2166+ assert_eq ! ( vmm. vcpus_handles. len( ) , 1 ) ;
2167+
2168+ // Case 3. Vcpu count too high
2169+ let mut vmm = default_vmm ( ) ;
2170+ vmm. hotplug_vcpus ( HotplugVcpuConfig { add : 1 } ) . unwrap ( ) ;
2171+ assert_eq ! ( vmm. vcpus_handles. len( ) , 1 ) ;
2172+ let config = HotplugVcpuConfig { add : 33 } ;
2173+ let result = vmm. hotplug_vcpus ( config) ;
2174+ result. unwrap_err ( ) ;
2175+ assert_eq ! ( vmm. vcpus_handles. len( ) , 1 ) ;
2176+
2177+ // Case 4. Attempted overflow of vcpus
2178+ let mut vmm = default_vmm ( ) ;
2179+ vmm. hotplug_vcpus ( HotplugVcpuConfig { add : 2 } ) . unwrap ( ) ;
2180+ assert_eq ! ( vmm. vcpus_handles. len( ) , 2 ) ;
2181+ let config = HotplugVcpuConfig { add : 255 } ;
2182+ let result = vmm. hotplug_vcpus ( config) ;
2183+ result. unwrap_err ( ) ;
2184+ assert_eq ! ( vmm. vcpus_handles. len( ) , 2 ) ;
2185+ }
2186+
20622187 #[ test]
20632188 fn test_runtime_disallowed ( ) {
20642189 check_runtime_request_err (
0 commit comments