Skip to content

Commit f84e4a5

Browse files
roypatShadowCurse
authored andcommitted
Use BTreeMap/Vec instead of HashMap/HashSet when dealing with MSRs
As the previous commit shows, the order of restoration of MSRs can be important. Thus, we should not effectively randomly shuffle them whenever we construct a VM. With this commit, we preserve the order in which KVM tells us about MSRs in `KVM_GET_MSR_INDEX_LIST`, under the assumption that if any dependencies exist, KVM will account for them as part of the above IOCTL. Signed-off-by: Patrick Roy <[email protected]>
1 parent acecc8d commit f84e4a5

File tree

5 files changed

+53
-23
lines changed

5 files changed

+53
-23
lines changed

src/cpu-template-helper/src/template/dump/x86_64.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
22
// SPDX-License-Identifier: Apache-2.0
33

4-
use std::collections::HashMap;
4+
use std::collections::BTreeMap;
55

66
use vmm::arch::x86_64::msr::MsrRange;
77
use vmm::arch_gen::x86::msr_index::*;
@@ -44,7 +44,7 @@ fn cpuid_to_modifiers(cpuid: &Cpuid) -> Vec<CpuidLeafModifier> {
4444
.collect()
4545
}
4646

47-
fn msrs_to_modifier(msrs: &HashMap<u32, u64>) -> Vec<RegisterModifier> {
47+
fn msrs_to_modifier(msrs: &BTreeMap<u32, u64>) -> Vec<RegisterModifier> {
4848
let mut msrs: Vec<RegisterModifier> = msrs
4949
.iter()
5050
.map(|(index, value)| msr_modifier!(*index, *value))
@@ -189,8 +189,8 @@ mod tests {
189189
]
190190
}
191191

192-
fn build_sample_msrs() -> HashMap<u32, u64> {
193-
let mut map = HashMap::from([
192+
fn build_sample_msrs() -> BTreeMap<u32, u64> {
193+
let mut map = BTreeMap::from([
194194
// should be sorted in the result.
195195
(0x1, 0xffff_ffff_ffff_ffff),
196196
(0x5, 0xffff_ffff_0000_0000),

src/vmm/src/cpu_config/x86_64/cpuid/common.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ pub fn get_vendor_id_from_host() -> Result<[u8; 12], GetCpuidError> {
5353
}
5454

5555
/// Returns MSRs to be saved based on CPUID features that are enabled.
56-
pub(crate) fn msrs_to_save_by_cpuid(cpuid: &kvm_bindings::CpuId) -> std::collections::HashSet<u32> {
56+
pub(crate) fn msrs_to_save_by_cpuid(cpuid: &kvm_bindings::CpuId) -> Vec<u32> {
5757
/// Memory Protection Extensions
5858
const MPX_BITINDEX: u32 = 14;
5959

@@ -81,7 +81,7 @@ pub(crate) fn msrs_to_save_by_cpuid(cpuid: &kvm_bindings::CpuId) -> std::collect
8181
}};
8282
}
8383

84-
let mut msrs = std::collections::HashSet::new();
84+
let mut msrs = Vec::new();
8585

8686
// Macro used for easy definition of CPUID-MSR dependencies.
8787
macro_rules! cpuid_msr_dep {

src/vmm/src/cpu_config/x86_64/mod.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ pub mod static_cpu_templates;
1010
/// Module with test utils for custom CPU templates
1111
pub mod test_utils;
1212

13-
use std::collections::HashMap;
13+
use std::collections::BTreeMap;
1414

1515
use self::custom_cpu_template::CpuidRegister;
1616
use super::templates::CustomCpuTemplate;
@@ -37,7 +37,7 @@ pub struct CpuConfiguration {
3737
/// Register values as a key pair for model specific registers
3838
/// Key: MSR address
3939
/// Value: MSR value
40-
pub msrs: HashMap<u32, u64>,
40+
pub msrs: BTreeMap<u32, u64>,
4141
}
4242

4343
impl CpuConfiguration {
@@ -187,14 +187,14 @@ mod tests {
187187
fn supported_cpu_config() -> CpuConfiguration {
188188
CpuConfiguration {
189189
cpuid: build_supported_cpuid(),
190-
msrs: HashMap::from([(0x8000, 0b1000), (0x9999, 0b1010)]),
190+
msrs: BTreeMap::from([(0x8000, 0b1000), (0x9999, 0b1010)]),
191191
}
192192
}
193193

194194
fn unsupported_cpu_config() -> CpuConfiguration {
195195
CpuConfiguration {
196196
cpuid: build_supported_cpuid(),
197-
msrs: HashMap::from([(0x8000, 0b1000), (0x8001, 0b1010)]),
197+
msrs: BTreeMap::from([(0x8000, 0b1000), (0x8001, 0b1010)]),
198198
}
199199
}
200200

src/vmm/src/vstate/vcpu/mod.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -694,6 +694,8 @@ pub enum VcpuEmulation {
694694
pub mod tests {
695695
#![allow(clippy::undocumented_unsafe_blocks)]
696696

697+
#[cfg(target_arch = "x86_64")]
698+
use std::collections::BTreeMap;
697699
use std::sync::{Arc, Barrier, Mutex};
698700

699701
use linux_loader::loader::KernelLoader;
@@ -919,7 +921,7 @@ pub mod tests {
919921
smt: false,
920922
cpu_config: CpuConfiguration {
921923
cpuid: Cpuid::try_from(_vm.supported_cpuid().clone()).unwrap(),
922-
msrs: std::collections::HashMap::new(),
924+
msrs: BTreeMap::new(),
923925
},
924926
},
925927
)

src/vmm/src/vstate/vcpu/x86_64.rs

Lines changed: 40 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
// Use of this source code is governed by a BSD-style license that can be
66
// found in the THIRD-PARTY file.
77

8-
use std::collections::{HashMap, HashSet};
8+
use std::collections::BTreeMap;
99
use std::fmt::Debug;
1010

1111
use kvm_bindings::{
@@ -143,7 +143,9 @@ pub struct KvmVcpu {
143143
pub fd: VcpuFd,
144144
/// Vcpu peripherals, such as buses
145145
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>,
147149
}
148150

149151
/// Vcpu peripherals
@@ -172,7 +174,7 @@ impl KvmVcpu {
172174
index,
173175
fd: kvm_vcpu,
174176
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(),
176178
})
177179
}
178180

@@ -416,8 +418,8 @@ impl KvmVcpu {
416418
/// # Errors
417419
///
418420
/// * When `KvmVcpu::get_msr_chunks()` returns errors.
419-
pub fn get_msrs(&self, msr_index_list: &[u32]) -> Result<HashMap<u32, u64>, KvmVcpuError> {
420-
let mut msrs: HashMap<u32, u64> = HashMap::new();
421+
pub fn get_msrs(&self, msr_index_list: &[u32]) -> Result<BTreeMap<u32, u64>, KvmVcpuError> {
422+
let mut msrs = BTreeMap::new();
421423
self.get_msr_chunks(msr_index_list)?
422424
.iter()
423425
.for_each(|msr_chunk| {
@@ -469,8 +471,7 @@ impl KvmVcpu {
469471
None
470472
});
471473
let cpuid = self.get_cpuid()?;
472-
let saved_msrs =
473-
self.get_msr_chunks(&self.msrs_to_save.iter().copied().collect::<Vec<_>>())?;
474+
let saved_msrs = self.get_msr_chunks(&self.msrs_to_save.to_vec())?;
474475
let vcpu_events = self
475476
.fd
476477
.get_vcpu_events()
@@ -678,7 +679,7 @@ mod tests {
678679
use std::os::unix::io::AsRawFd;
679680

680681
use kvm_bindings::kvm_msr_entry;
681-
use kvm_ioctls::Cap;
682+
use kvm_ioctls::{Cap, Kvm};
682683

683684
use super::*;
684685
use crate::arch::x86_64::cpu_model::CpuModel;
@@ -863,7 +864,7 @@ mod tests {
863864
smt: false,
864865
cpu_config: CpuConfiguration {
865866
cpuid: Cpuid::try_from(vm.supported_cpuid().clone()).unwrap(),
866-
msrs: HashMap::new(),
867+
msrs: BTreeMap::new(),
867868
},
868869
};
869870
vcpu.configure(&vm_mem, GuestAddress(0), &vcpu_config)
@@ -925,7 +926,7 @@ mod tests {
925926
smt: false,
926927
cpu_config: CpuConfiguration {
927928
cpuid: Cpuid::try_from(vm.supported_cpuid().clone()).unwrap(),
928-
msrs: HashMap::new(),
929+
msrs: BTreeMap::new(),
929930
},
930931
};
931932
vcpu.configure(&vm_mem, GuestAddress(0), &vcpu_config)
@@ -1003,8 +1004,7 @@ mod tests {
10031004
// Test `get_msrs()` with the MSR indices that should be serialized into snapshots.
10041005
// The MSR indices should be valid and this test should succeed.
10051006
let (_, vcpu, _) = setup_vcpu(0x1000);
1006-
vcpu.get_msrs(&vcpu.msrs_to_save.iter().copied().collect::<Vec<_>>())
1007-
.unwrap();
1007+
vcpu.get_msrs(&vcpu.msrs_to_save.to_vec()).unwrap();
10081008
}
10091009

10101010
#[test]
@@ -1125,4 +1125,32 @@ mod tests {
11251125
&[(MSR_IA32_TSC_DEADLINE, 1), (MSR_IA32_TSC, 2)],
11261126
);
11271127
}
1128+
1129+
#[test]
1130+
fn test_get_msr_chunks_preserved_order() {
1131+
// Regression test for #4666
1132+
1133+
let kvm = Kvm::new().unwrap();
1134+
let vm = Vm::new(Vec::new()).unwrap();
1135+
let vcpu = KvmVcpu::new(0, &vm).unwrap();
1136+
1137+
// The list of supported MSR indices, in the order they were returned by KVM
1138+
let msrs_to_save = crate::arch::x86_64::msr::get_msrs_to_save(&kvm).unwrap();
1139+
// The MSRs after processing. The order should be identical to the one returned by KVM, with
1140+
// the exception of deferred MSRs, which should be moved to the end (but show up in the same
1141+
// order as they are listed in [`DEFERRED_MSRS`].
1142+
let msr_chunks = vcpu.get_msr_chunks(&vcpu.msrs_to_save).unwrap();
1143+
1144+
msr_chunks
1145+
.iter()
1146+
.flat_map(|chunk| chunk.as_slice().iter())
1147+
.zip(
1148+
msrs_to_save
1149+
.as_slice()
1150+
.iter()
1151+
.filter(|&idx| !DEFERRED_MSRS.contains(idx))
1152+
.chain(DEFERRED_MSRS.iter()),
1153+
)
1154+
.for_each(|(left, &right)| assert_eq!(left.index, right));
1155+
}
11281156
}

0 commit comments

Comments
 (0)