@@ -600,6 +600,92 @@ impl Vmm {
600
600
Ok ( cpu_configs)
601
601
}
602
602
603
+ /// Adds new vCPUs to VMM.
604
+ #[ cfg( target_arch = "x86_64" ) ]
605
+ pub fn hotplug_vcpus (
606
+ & mut self ,
607
+ config : HotplugVcpuConfig ,
608
+ ) -> Result < MachineConfigUpdate , HotplugVcpuError > {
609
+ use crate :: logger:: IncMetric ;
610
+ if config. add < 1 {
611
+ return Err ( HotplugVcpuError :: VcpuCountTooLow ) ;
612
+ } else if self
613
+ . vcpus_handles
614
+ . len ( )
615
+ . checked_add ( config. add . into ( ) )
616
+ . ok_or ( HotplugVcpuError :: VcpuCountTooHigh ) ?
617
+ > MAX_SUPPORTED_VCPUS . into ( )
618
+ {
619
+ return Err ( HotplugVcpuError :: VcpuCountTooHigh ) ;
620
+ }
621
+
622
+ if let Some ( kvm_config) = self . vcpu_config . as_mut ( ) {
623
+ kvm_config. vcpu_count += config. add ;
624
+ }
625
+ // Create and start new vcpus
626
+ let mut vcpus = Vec :: with_capacity ( config. add . into ( ) ) ;
627
+
628
+ #[ allow( clippy:: cast_possible_truncation) ]
629
+ let start_idx = self . vcpus_handles . len ( ) . try_into ( ) . unwrap ( ) ;
630
+ if let Some ( devices:: BusDevice :: CpuContainer ( cont) ) =
631
+ self . get_bus_device ( DeviceType :: CpuContainer , "CpuContainer" )
632
+ {
633
+ let mut locked_container = cont. lock ( ) . expect ( "Poisoned lock" ) ;
634
+ for cpu_idx in start_idx..( start_idx + config. add ) {
635
+ let exit_evt = self
636
+ . vcpus_exit_evt
637
+ . try_clone ( )
638
+ . map_err ( HotplugVcpuError :: EventFd ) ?;
639
+ let mut vcpu =
640
+ Vcpu :: new ( cpu_idx, & self . vm , exit_evt) . map_err ( HotplugVcpuError :: VcpuCreate ) ?;
641
+ if let Some ( kvm_config) = self . vcpu_config . as_ref ( ) {
642
+ vcpu. kvm_vcpu . hotplug_configure ( kvm_config) ?;
643
+ } else {
644
+ return Err ( HotplugVcpuError :: RestoredFromSnapshot ) ;
645
+ }
646
+ locked_container. cpu_devices [ cpu_idx as usize ] . inserting = true ;
647
+ vcpus. push ( vcpu) ;
648
+ }
649
+ }
650
+
651
+ self . start_vcpus (
652
+ vcpus,
653
+ self . seccomp_filters
654
+ . get ( "vcpu" )
655
+ . ok_or_else ( || HotplugVcpuError :: MissingSeccompFilters ( "vcpu" . to_string ( ) ) ) ?
656
+ . clone ( ) ,
657
+ )
658
+ . map_err ( HotplugVcpuError :: VcpuStart ) ?;
659
+
660
+ #[ allow( clippy:: cast_lossless) ]
661
+ METRICS . hotplug . vcpus_added . add ( config. add . into ( ) ) ;
662
+
663
+ // Update VM config to reflect new CPUs added
664
+ #[ allow( clippy:: cast_possible_truncation) ]
665
+ let new_machine_config = MachineConfigUpdate {
666
+ vcpu_count : Some ( self . vcpus_handles . len ( ) as u8 ) ,
667
+ mem_size_mib : None ,
668
+ smt : None ,
669
+ cpu_template : None ,
670
+ track_dirty_pages : None ,
671
+ huge_pages : None ,
672
+ } ;
673
+
674
+ self . resume_vcpu_threads ( start_idx. into ( ) ) ?;
675
+
676
+ self . acpi_device_manager . notify_cpu_container ( ) ?;
677
+
678
+ #[ cfg( test) ]
679
+ if let Some ( devices:: BusDevice :: BootTimer ( timer) ) =
680
+ self . get_bus_device ( DeviceType :: BootTimer , "BootTimer" )
681
+ {
682
+ let mut locked_timer = timer. lock ( ) . expect ( "Poisoned lock" ) ;
683
+ locked_timer. start_ts = utils:: time:: TimestampUs :: default ( ) ;
684
+ }
685
+
686
+ Ok ( new_machine_config)
687
+ }
688
+
603
689
/// Retrieves the KVM dirty bitmap for each of the guest's memory regions.
604
690
pub fn reset_dirty_bitmap ( & self ) {
605
691
self . guest_memory
0 commit comments