@@ -670,7 +670,7 @@ impl RuntimeApiController {
670
670
self . vmm . lock ( ) . expect ( "Poisoned lock" ) . version ( ) ,
671
671
) ) ,
672
672
#[ cfg( target_arch = "x86_64" ) ]
673
- HotplugRequest ( config ) => Ok ( VmmData :: Empty ) ,
673
+ HotplugRequest ( request_type ) => self . handle_hotplug_request ( request_type ) ,
674
674
PatchMMDS ( value) => self . patch_mmds ( value) ,
675
675
Pause => self . pause ( ) ,
676
676
PutMMDS ( value) => self . put_mmds ( value) ,
@@ -867,6 +867,31 @@ impl RuntimeApiController {
867
867
. map_err ( NetworkInterfaceError :: DeviceUpdate )
868
868
. map_err ( VmmActionError :: NetworkConfig )
869
869
}
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
+ }
870
895
}
871
896
872
897
#[ cfg( test) ]
@@ -875,8 +900,14 @@ mod tests {
875
900
use std:: path:: PathBuf ;
876
901
877
902
use seccompiler:: BpfThreadMap ;
903
+ #[ cfg( target_arch = "x86_64" ) ]
904
+ use vmm_config:: hotplug:: HotplugVcpuError ;
878
905
879
906
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;
880
911
use crate :: cpu_config:: templates:: test_utils:: build_test_template;
881
912
use crate :: cpu_config:: templates:: { CpuTemplateType , StaticCpuTemplate } ;
882
913
use crate :: devices:: virtio:: balloon:: { BalloonConfig , BalloonError } ;
@@ -885,6 +916,8 @@ mod tests {
885
916
use crate :: devices:: virtio:: vsock:: VsockError ;
886
917
use crate :: mmds:: data_store:: MmdsVersion ;
887
918
use crate :: vmm_config:: balloon:: BalloonBuilder ;
919
+ #[ cfg( target_arch = "x86_64" ) ]
920
+ use crate :: vmm_config:: hotplug:: HotplugVcpuConfig ;
888
921
use crate :: vmm_config:: machine_config:: VmConfig ;
889
922
use crate :: vmm_config:: snapshot:: { MemBackendConfig , MemBackendType } ;
890
923
use crate :: vmm_config:: vsock:: VsockBuilder ;
@@ -893,27 +926,55 @@ mod tests {
893
926
impl PartialEq for VmmActionError {
894
927
fn eq ( & self , other : & VmmActionError ) -> bool {
895
928
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
+ }
917
978
}
918
979
}
919
980
@@ -1106,6 +1167,8 @@ mod tests {
1106
1167
pub update_block_device_path_called : bool ,
1107
1168
pub update_block_device_vhost_user_config_called : bool ,
1108
1169
pub update_net_rate_limiters_called : bool ,
1170
+ #[ cfg( target_arch = "x86_64" ) ]
1171
+ pub hotplug_vcpus_called : bool ,
1109
1172
// when `true`, all self methods are forced to fail
1110
1173
pub force_errors : bool ,
1111
1174
}
@@ -1216,6 +1279,24 @@ mod tests {
1216
1279
Ok ( ( ) )
1217
1280
}
1218
1281
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
+ }
1219
1300
pub fn instance_info ( & self ) -> InstanceInfo {
1220
1301
InstanceInfo :: default ( )
1221
1302
}
@@ -1830,6 +1911,12 @@ mod tests {
1830
1911
VmmAction :: SendCtrlAltDel ,
1831
1912
VmmActionError :: OperationNotSupportedPreBoot ,
1832
1913
) ;
1914
+
1915
+ #[ cfg( target_arch = "x86_64" ) ]
1916
+ check_preboot_request_err (
1917
+ VmmAction :: HotplugRequest ( HotplugRequestConfig :: Vcpu ( HotplugVcpuConfig { add : 4 } ) ) ,
1918
+ VmmActionError :: OperationNotSupportedPreBoot ,
1919
+ ) ;
1833
1920
}
1834
1921
1835
1922
fn check_runtime_request < F > ( request : VmmAction , check_success : F )
@@ -2059,6 +2146,44 @@ mod tests {
2059
2146
) ;
2060
2147
}
2061
2148
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
+
2062
2187
#[ test]
2063
2188
fn test_runtime_disallowed ( ) {
2064
2189
check_runtime_request_err (
0 commit comments