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