@@ -124,11 +124,17 @@ use device_manager::resources::ResourceAllocator;
124
124
use devices:: acpi:: vmgenid:: VmGenIdError ;
125
125
use event_manager:: { EventManager as BaseEventManager , EventOps , Events , MutEventSubscriber } ;
126
126
use seccompiler:: BpfProgram ;
127
+ #[ cfg( target_arch = "x86_64" ) ]
128
+ use seccompiler:: BpfThreadMap ;
127
129
use userfaultfd:: Uffd ;
128
130
use utils:: epoll:: EventSet ;
129
131
use utils:: eventfd:: EventFd ;
130
132
use utils:: terminal:: Terminal ;
131
133
use utils:: u64_to_usize;
134
+ #[ cfg( target_arch = "x86_64" ) ]
135
+ use vmm_config:: hotplug:: { HotplugVcpuConfig , HotplugVcpuError } ;
136
+ #[ cfg( target_arch = "x86_64" ) ]
137
+ use vmm_config:: machine_config:: { MachineConfigUpdate , MAX_SUPPORTED_VCPUS } ;
132
138
use vstate:: vcpu:: { self , KvmVcpuConfigureError , StartThreadedError , VcpuSendEventError } ;
133
139
134
140
use crate :: arch:: DeviceType ;
@@ -317,6 +323,9 @@ pub struct Vmm {
317
323
vcpus_handles : Vec < VcpuHandle > ,
318
324
// Used by Vcpus and devices to initiate teardown; Vmm should never write here.
319
325
vcpus_exit_evt : EventFd ,
326
+ // seccomp_filters are only needed in VMM for hotplugging vCPUS.
327
+ #[ cfg( target_arch = "x86_64" ) ]
328
+ seccomp_filters : BpfThreadMap ,
320
329
321
330
// Allocator for guest resrouces
322
331
resource_allocator : ResourceAllocator ,
@@ -600,6 +609,66 @@ impl Vmm {
600
609
Ok ( cpu_configs)
601
610
}
602
611
612
+ /// Adds new vCPUs to VMM.
613
+ #[ cfg( target_arch = "x86_64" ) ]
614
+ pub fn hotplug_vcpus (
615
+ & mut self ,
616
+ config : HotplugVcpuConfig ,
617
+ ) -> Result < MachineConfigUpdate , HotplugVcpuError > {
618
+ use crate :: logger:: IncMetric ;
619
+ if config. vcpu_count < 1 {
620
+ return Err ( HotplugVcpuError :: VcpuCountTooLow ) ;
621
+ } else if self
622
+ . vcpus_handles
623
+ . len ( )
624
+ . checked_add ( config. vcpu_count . into ( ) )
625
+ . ok_or ( HotplugVcpuError :: VcpuCountTooHigh ) ?
626
+ > MAX_SUPPORTED_VCPUS . into ( )
627
+ {
628
+ return Err ( HotplugVcpuError :: VcpuCountTooHigh ) ;
629
+ }
630
+
631
+ // Create and start new vcpus
632
+ let mut vcpus = Vec :: with_capacity ( config. vcpu_count . into ( ) ) ;
633
+
634
+ #[ allow( clippy:: cast_possible_truncation) ]
635
+ let start_idx = self . vcpus_handles . len ( ) . try_into ( ) . unwrap ( ) ;
636
+ for cpu_idx in start_idx..( start_idx + config. vcpu_count ) {
637
+ let exit_evt = self
638
+ . vcpus_exit_evt
639
+ . try_clone ( )
640
+ . map_err ( HotplugVcpuError :: EventFd ) ?;
641
+ let vcpu =
642
+ Vcpu :: new ( cpu_idx, & self . vm , exit_evt) . map_err ( HotplugVcpuError :: VcpuCreate ) ?;
643
+ vcpus. push ( vcpu) ;
644
+ }
645
+
646
+ self . start_vcpus (
647
+ vcpus,
648
+ self . seccomp_filters
649
+ . get ( "vcpu" )
650
+ . ok_or_else ( || HotplugVcpuError :: MissingSeccompFilters ( "vcpu" . to_string ( ) ) ) ?
651
+ . clone ( ) ,
652
+ )
653
+ . map_err ( HotplugVcpuError :: VcpuStart ) ?;
654
+
655
+ #[ allow( clippy:: cast_lossless) ]
656
+ METRICS . hotplug . vcpus_added . add ( config. vcpu_count . into ( ) ) ;
657
+
658
+ // Update VM config to reflect new CPUs added
659
+ #[ allow( clippy:: cast_possible_truncation) ]
660
+ let new_machine_config = MachineConfigUpdate {
661
+ vcpu_count : Some ( self . vcpus_handles . len ( ) as u8 ) ,
662
+ mem_size_mib : None ,
663
+ smt : None ,
664
+ cpu_template : None ,
665
+ track_dirty_pages : None ,
666
+ huge_pages : None ,
667
+ } ;
668
+
669
+ Ok ( new_machine_config)
670
+ }
671
+
603
672
/// Retrieves the KVM dirty bitmap for each of the guest's memory regions.
604
673
pub fn reset_dirty_bitmap ( & self ) {
605
674
self . guest_memory
0 commit comments