Repository: firecracker-microvm/firecracker
File: src/vmm/src/vstate/vcpu/x86_64.rs (Rust)

Code before:
    /// the IA32_TSC_DEADLINE MSR is cleared, but the interrupt is not
    /// delivered to the guest, leading to a situation where one
    /// of the vCPUs never receives TSC interrupts after restoring,
    /// until the MSR is updated externally, eg by setting the system time.
    fn fix_zero_tsc_deadline_msr(msr_chunks: &mut [Msrs]) {
        for msrs in msr_chunks.iter_mut() {
            let tsc_value = msrs
                .as_slice()
                .iter()
                .find(|msr| msr.index == MSR_IA32_TSC)
                .map(|msr| msr.data)
                .unwrap_or(0);

            for msr in msrs.as_mut_slice().iter_mut() {
                if msr.index == MSR_IA32_TSC_DEADLINE && msr.data == 0 {
                    warn!(
                        "MSR_IA32_TSC_DEADLINE is 0, replacing with {:x}.",
                        tsc_value
                    );
                    msr.data = tsc_value;
                }
            }
        }
    }

    /// Get MSR chunks for the given MSR index list.
    ///
    /// KVM only supports getting `KVM_MAX_MSR_ENTRIES` at a time, so we divide
    /// the list of MSR indices into chunks, call `KVM_GET_MSRS` for each
    /// chunk, and collect into a [`Vec<Msrs>`].
    ///
    /// # Arguments
    ///
    /// * `msr_index_list`: List of MSR indices.
    ///
    /// # Errors
    ///
    /// * When [`kvm_bindings::Msrs::new`] returns errors.
    /// * When [`kvm_ioctls::VcpuFd::get_msrs`] returns errors.
    /// * When the return value of [`kvm_ioctls::VcpuFd::get_msrs`] (the number of entries that
    ///   could be gotten) is less than expected.
    fn get_msr_chunks(&self, msr_index_list: &[u32]) -> Result<Vec<Msrs>, KvmVcpuError> {
        let mut msr_chunks: Vec<Msrs> = Vec::new();

        for msr_index_chunk in msr_index_list.chunks(KVM_MAX_MSR_ENTRIES) {
            let mut msrs = Msrs::new(msr_index_chunk.len())?;
            let msr_entries = msrs.as_mut_slice();
            assert_eq!(msr_index_chunk.len(), msr_entries.len());
            for (pos, index) in msr_index_chunk.iter().enumerate() {
                msr_entries[pos].index = *index;
            }

Review comment (performance):
Does it take very long to add this loop? I guess we will see it in the snapshot performance but just to get an idea if this is going to be a regression.

Code after:
    /// the IA32_TSC_DEADLINE MSR is cleared, but the interrupt is not
    /// delivered to the guest, leading to a situation where one
    /// of the vCPUs never receives TSC interrupts after restoring,
    /// until the MSR is updated externally, eg by setting the system time.
    fn fix_zero_tsc_deadline_msr(msr_chunks: &mut [Msrs]) {
        // We do not expect more than 1 TSC MSR entry, but if there are multiple, pick the maximum.
        let max_tsc_value = msr_chunks
            .iter()
            .flat_map(|msrs| msrs.as_slice())
            .filter(|msr| msr.index == MSR_IA32_TSC)
            .map(|msr| msr.data)
            .max();

        if let Some(tsc_value) = max_tsc_value {
            msr_chunks
                .iter_mut()
                .flat_map(|msrs| msrs.as_mut_slice())
                .filter(|msr| msr.index == MSR_IA32_TSC_DEADLINE && msr.data == 0)
                .for_each(|msr| {
                    warn!(
                        "MSR_IA32_TSC_DEADLINE is 0, replacing with {:x}.",
                        tsc_value
                    );
                    msr.data = tsc_value;
                });
        }
    }

    /// Get MSR chunks for the given MSR index list.
    ///
    /// KVM only supports getting `KVM_MAX_MSR_ENTRIES` at a time, so we divide
    /// the list of MSR indices into chunks, call `KVM_GET_MSRS` for each
    /// chunk, and collect into a [`Vec<Msrs>`].
    ///
    /// # Arguments
    ///
    /// * `msr_index_list`: List of MSR indices.
    ///
    /// # Errors
    ///
    /// * When [`kvm_bindings::Msrs::new`] returns errors.
    /// * When [`kvm_ioctls::VcpuFd::get_msrs`] returns errors.
    /// * When the return value of [`kvm_ioctls::VcpuFd::get_msrs`] (the number of entries that
    ///   could be gotten) is less than expected.
    fn get_msr_chunks(&self, msr_index_list: &[u32]) -> Result<Vec<Msrs>, KvmVcpuError> {
        let mut msr_chunks: Vec<Msrs> = Vec::new();

        for msr_index_chunk in msr_index_list.chunks(KVM_MAX_MSR_ENTRIES) {
            let mut msrs = Msrs::new(msr_index_chunk.len())?;
            let msr_entries = msrs.as_mut_slice();
            assert_eq!(msr_index_chunk.len(), msr_entries.len());