|
5 | 5 | // Use of this source code is governed by a BSD-style license that can be |
6 | 6 | // found in the THIRD-PARTY file. |
7 | 7 |
|
8 | | -use std::collections::{HashMap, HashSet}; |
| 8 | +use std::collections::BTreeMap; |
9 | 9 | use std::fmt::Debug; |
10 | 10 |
|
11 | 11 | use kvm_bindings::{ |
@@ -143,7 +143,9 @@ pub struct KvmVcpu { |
143 | 143 | pub fd: VcpuFd, |
144 | 144 | /// Vcpu peripherals, such as buses |
145 | 145 | pub(super) peripherals: Peripherals, |
146 | | - msrs_to_save: HashSet<u32>, |
| 146 | + /// The list of MSRs to include in a VM snapshot, in the same order as KVM returned them |
| 147 | + /// from KVM_GET_MSR_INDEX_LIST |
| 148 | + msrs_to_save: Vec<u32>, |
147 | 149 | } |
148 | 150 |
|
149 | 151 | /// Vcpu peripherals |
@@ -172,7 +174,7 @@ impl KvmVcpu { |
172 | 174 | index, |
173 | 175 | fd: kvm_vcpu, |
174 | 176 | peripherals: Default::default(), |
175 | | - msrs_to_save: vm.msrs_to_save().as_slice().iter().copied().collect(), |
| 177 | + msrs_to_save: vm.msrs_to_save().as_slice().to_vec(), |
176 | 178 | }) |
177 | 179 | } |
178 | 180 |
|
@@ -455,8 +457,8 @@ impl KvmVcpu { |
455 | 457 | pub fn get_msrs( |
456 | 458 | &self, |
457 | 459 | msr_index_iter: impl ExactSizeIterator<Item = u32>, |
458 | | - ) -> Result<HashMap<u32, u64>, KvmVcpuError> { |
459 | | - let mut msrs: HashMap<u32, u64> = HashMap::new(); |
| 460 | + ) -> Result<BTreeMap<u32, u64>, KvmVcpuError> { |
| 461 | + let mut msrs = BTreeMap::new(); |
460 | 462 | self.get_msr_chunks(msr_index_iter)? |
461 | 463 | .iter() |
462 | 464 | .for_each(|msr_chunk| { |
@@ -716,7 +718,7 @@ mod tests { |
716 | 718 | use std::os::unix::io::AsRawFd; |
717 | 719 |
|
718 | 720 | use kvm_bindings::kvm_msr_entry; |
719 | | - use kvm_ioctls::Cap; |
| 721 | + use kvm_ioctls::{Cap, Kvm}; |
720 | 722 |
|
721 | 723 | use super::*; |
722 | 724 | use crate::arch::x86_64::cpu_model::CpuModel; |
@@ -901,7 +903,7 @@ mod tests { |
901 | 903 | smt: false, |
902 | 904 | cpu_config: CpuConfiguration { |
903 | 905 | cpuid: Cpuid::try_from(vm.supported_cpuid().clone()).unwrap(), |
904 | | - msrs: HashMap::new(), |
| 906 | + msrs: BTreeMap::new(), |
905 | 907 | }, |
906 | 908 | }; |
907 | 909 | vcpu.configure(&vm_mem, GuestAddress(0), &vcpu_config) |
@@ -963,7 +965,7 @@ mod tests { |
963 | 965 | smt: false, |
964 | 966 | cpu_config: CpuConfiguration { |
965 | 967 | cpuid: Cpuid::try_from(vm.supported_cpuid().clone()).unwrap(), |
966 | | - msrs: HashMap::new(), |
| 968 | + msrs: BTreeMap::new(), |
967 | 969 | }, |
968 | 970 | }; |
969 | 971 | vcpu.configure(&vm_mem, GuestAddress(0), &vcpu_config) |
@@ -1163,4 +1165,34 @@ mod tests { |
1163 | 1165 | &[(MSR_IA32_TSC_DEADLINE, 1), (MSR_IA32_TSC, 2)], |
1164 | 1166 | ); |
1165 | 1167 | } |
| 1168 | + |
| 1169 | + #[test] |
| 1170 | + fn test_get_msr_chunks_preserved_order() { |
| 1171 | + // Regression test for #4666 |
| 1172 | + |
| 1173 | + let kvm = Kvm::new().unwrap(); |
| 1174 | + let vm = Vm::new(Vec::new()).unwrap(); |
| 1175 | + let vcpu = KvmVcpu::new(0, &vm).unwrap(); |
| 1176 | + |
| 1177 | + // The list of supported MSR indices, in the order they were returned by KVM |
| 1178 | + let msrs_to_save = crate::arch::x86_64::msr::get_msrs_to_save(&kvm).unwrap(); |
| 1179 | + // The MSRs after processing. The order should be identical to the one returned by KVM, with |
| 1180 | + // the exception of deferred MSRs, which should be moved to the end (but show up in the same |
| 1181 | + // order as they are listed in [`DEFERRED_MSRS`]. |
| 1182 | + let msr_chunks = vcpu |
| 1183 | + .get_msr_chunks(vcpu.msrs_to_save.iter().copied()) |
| 1184 | + .unwrap(); |
| 1185 | + |
| 1186 | + msr_chunks |
| 1187 | + .iter() |
| 1188 | + .flat_map(|chunk| chunk.as_slice().iter()) |
| 1189 | + .zip( |
| 1190 | + msrs_to_save |
| 1191 | + .as_slice() |
| 1192 | + .iter() |
| 1193 | + .filter(|&idx| !DEFERRED_MSRS.contains(idx)) |
| 1194 | + .chain(DEFERRED_MSRS.iter()), |
| 1195 | + ) |
| 1196 | + .for_each(|(left, &right)| assert_eq!(left.index, right)); |
| 1197 | + } |
1166 | 1198 | } |
0 commit comments