Repository: firecracker-microvm/firecracker
File: src/vmm/src/lib.rs (Rust)

Code before:
    pub fn hotplug_vcpus(&mut self, config: HotplugVcpuConfig) -> Result<(), HotplugVcpuError> {
        println!("Num vCPUs before hotplug: {}", self.vcpus_handles.len());

        if config.vcpu_count < 1 {
            return Err(HotplugVcpuError::VcpuCountTooLow);
        } else if self.vcpus_handles.len() as u8 + config.vcpu_count > MAX_SUPPORTED_VCPUS {
            return Err(HotplugVcpuError::VcpuCountTooHigh);
        }

        // Create and start new vcpus
        let mut vcpus = Vec::with_capacity(config.vcpu_count as usize);

        let start_idx = self.vcpus_handles.len() as u8;
        for cpu_idx in start_idx..(start_idx + config.vcpu_count) {
            let exit_evt = self
                .vcpus_exit_evt
                .try_clone()
                .map_err(HotplugVcpuError::EventFd)?;
            let vcpu =
                Vcpu::new(cpu_idx, &self.vm, exit_evt).map_err(HotplugVcpuError::VcpuCreate)?;
            vcpus.push(vcpu);
        }

        let seccomp_filters = get_empty_filters();
        self.start_vcpus(
            vcpus,
            seccomp_filters
                .get("vcpu")
                .unwrap()
                // .ok_or_else(|| VmmError::SeccompFilters(()))?
                .clone(),
        )
        .map_err(HotplugVcpuError::VcpuStart)?;

        println!("Num vCPUs after hotplug: {}", self.vcpus_handles.len());

        // Update VM config to reflect new CPUs added

        // Resume VM
        self.resume_vm().unwrap();
        Ok(())
    }

    /// Retrieves the KVM dirty bitmap for each of the guest's memory regions.
    pub fn reset_dirty_bitmap(&self) {
        self.guest_memory
            .iter()
            .enumerate()
            .for_each(|(slot, region)| {
                let _ = self
                    .vm

Review comment (refactor):
Here, we should use the same seccomp filters as on the other vcpu threads. The problem here is Firecracker currently doesn't store the `BpfThreadMap` object post-boot, I think. In that case, we'll need to do some slight refactoring to store it and use it here

Code after:
            })
            .collect::<Result<Vec<CpuConfiguration>, DumpCpuConfigError>>()?;

        Ok(cpu_configs)
    }

    /// Adds new vCPUs to VMM.
    #[cfg(target_arch = "x86_64")]
    pub fn hotplug_vcpus(
        &mut self,
        config: HotplugVcpuConfig,
    ) -> Result<MachineConfigUpdate, HotplugVcpuError> {
        use crate::logger::IncMetric;
        if config.add < 1 {
            return Err(HotplugVcpuError::VcpuCountTooLow);
        } else if self
            .vcpus_handles
            .len()
            .checked_add(config.add.into())
            .ok_or(HotplugVcpuError::VcpuCountTooHigh)?
            > MAX_SUPPORTED_VCPUS.into()
        {
            return Err(HotplugVcpuError::VcpuCountTooHigh);
        }

        // Create and start new vcpus
        let mut vcpus = Vec::with_capacity(config.add.into());

        #[allow(clippy::cast_possible_truncation)]
        let start_idx = self.vcpus_handles.len().try_into().unwrap();
        for cpu_idx in start_idx..(start_idx + config.add) {
            let exit_evt = self
                .vcpus_exit_evt
                .try_clone()
                .map_err(HotplugVcpuError::EventFd)?;
            let vcpu =
                Vcpu::new(cpu_idx, &self.vm, exit_evt).map_err(HotplugVcpuError::VcpuCreate)?;
            vcpus.push(vcpu);
        }

        self.start_vcpus(
            vcpus,
            self.seccomp_filters
                .get("vcpu")
                .ok_or_else(|| HotplugVcpuError::MissingSeccompFilters("vcpu".to_string()))?
                .clone(),
        )
        .map_err(HotplugVcpuError::VcpuStart)?;

        #[allow(clippy::cast_lossless)]
        METRICS.hotplug.vcpus_added.add(config.add.into());