Skip to content

Commit 3f969b3

Browse files
roypatpb8o
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 b5bb8ec commit 3f969b3

File tree

5 files changed

+53
-19
lines changed

5 files changed

+53
-19
lines changed

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

+4-4
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

+2-2
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

+4-4
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

+3-1
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

+40-8
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

@@ -455,8 +457,8 @@ impl KvmVcpu {
455457
pub fn get_msrs(
456458
&self,
457459
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();
460462
self.get_msr_chunks(msr_index_iter)?
461463
.iter()
462464
.for_each(|msr_chunk| {
@@ -716,7 +718,7 @@ mod tests {
716718
use std::os::unix::io::AsRawFd;
717719

718720
use kvm_bindings::kvm_msr_entry;
719-
use kvm_ioctls::Cap;
721+
use kvm_ioctls::{Cap, Kvm};
720722

721723
use super::*;
722724
use crate::arch::x86_64::cpu_model::CpuModel;
@@ -901,7 +903,7 @@ mod tests {
901903
smt: false,
902904
cpu_config: CpuConfiguration {
903905
cpuid: Cpuid::try_from(vm.supported_cpuid().clone()).unwrap(),
904-
msrs: HashMap::new(),
906+
msrs: BTreeMap::new(),
905907
},
906908
};
907909
vcpu.configure(&vm_mem, GuestAddress(0), &vcpu_config)
@@ -963,7 +965,7 @@ mod tests {
963965
smt: false,
964966
cpu_config: CpuConfiguration {
965967
cpuid: Cpuid::try_from(vm.supported_cpuid().clone()).unwrap(),
966-
msrs: HashMap::new(),
968+
msrs: BTreeMap::new(),
967969
},
968970
};
969971
vcpu.configure(&vm_mem, GuestAddress(0), &vcpu_config)
@@ -1163,4 +1165,34 @@ mod tests {
11631165
&[(MSR_IA32_TSC_DEADLINE, 1), (MSR_IA32_TSC, 2)],
11641166
);
11651167
}
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+
}
11661198
}

0 commit comments

Comments
 (0)