|
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