Skip to content

Commit 034dae0

Browse files
committed
refactor: x64: use kvm_bindings::Msrs type directly in CpuConfiguration
Similarly to the previous commit, replace Btree type for Msrs handling with kvm_bindings::Msrs to remove the unnecessary state transitions and memory allocations. Uniqueness of entries is preserved with `msrs_insert` function. Together with previous commit this reduces the binary sizy by ~30KiB and reduces the maximum RSS during `test_all_vcpus_online` test by 50-80 KiB Signed-off-by: Egor Lazarchuk <yegorlz@amazon.co.uk>
1 parent a19fc16 commit 034dae0

6 files changed

Lines changed: 127 additions & 78 deletions

File tree

src/cpu-template-helper/Cargo.toml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ tracing = ["log-instrument", "vmm/tracing"]
1515
[dependencies]
1616
clap = { version = "4.6.1", features = ["derive", "string"] }
1717
displaydoc = "0.2.5"
18+
kvm-bindings = "0.14.0"
1819
libc = "0.2.186"
1920
log-instrument = { path = "../log-instrument", optional = true }
2021
serde = { version = "1.0.228", features = ["derive"] }
@@ -26,6 +27,3 @@ vmm-sys-util = "0.15.0"
2627

2728
[lints]
2829
workspace = true
29-
30-
[dev-dependencies]
31-
kvm-bindings = "0.14.0"

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

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +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::BTreeMap;
5-
4+
use kvm_bindings::Msrs;
65
use vmm::MSR_RANGE;
76
use vmm::arch::x86_64::generated::msr_index::*;
87
use vmm::arch::x86_64::msr::MsrRange;
@@ -45,10 +44,11 @@ fn cpuid_to_modifiers(cpuid: &Cpuid) -> Vec<CpuidLeafModifier> {
4544
.collect()
4645
}
4746

48-
fn msrs_to_modifier(msrs: &BTreeMap<u32, u64>) -> Vec<RegisterModifier> {
47+
fn msrs_to_modifier(msrs: &Msrs) -> Vec<RegisterModifier> {
4948
let mut msrs: Vec<RegisterModifier> = msrs
49+
.as_slice()
5050
.iter()
51-
.map(|(index, value)| msr_modifier!(*index, *value))
51+
.map(|entry| msr_modifier!(entry.index, entry.data))
5252
.collect();
5353

5454
msrs.retain(|modifier| !should_exclude_msr(modifier.addr));
@@ -120,8 +120,7 @@ fn should_exclude_msr_amd(index: u32) -> bool {
120120

121121
#[cfg(test)]
122122
mod tests {
123-
use std::collections::BTreeMap;
124-
123+
use kvm_bindings::kvm_msr_entry;
125124
use vmm::cpu_config::x86_64::cpuid::IntelCpuid;
126125

127126
use super::*;
@@ -181,24 +180,29 @@ mod tests {
181180
]
182181
}
183182

184-
fn build_sample_msrs() -> BTreeMap<u32, u64> {
185-
let mut map = BTreeMap::from([
183+
fn build_sample_msrs() -> Msrs {
184+
let entry = |index, data| kvm_msr_entry {
185+
index,
186+
data,
187+
..Default::default()
188+
};
189+
let mut entries = vec![
186190
// should be sorted in the result.
187-
(0x1, 0xffff_ffff_ffff_ffff),
188-
(0x5, 0xffff_ffff_0000_0000),
189-
(0x3, 0x0000_0000_ffff_ffff),
190-
(0x2, 0x0000_0000_0000_0000),
191-
]);
191+
entry(0x1, 0xffff_ffff_ffff_ffff),
192+
entry(0x5, 0xffff_ffff_0000_0000),
193+
entry(0x3, 0x0000_0000_ffff_ffff),
194+
entry(0x2, 0x0000_0000_0000_0000),
195+
];
192196
// should be excluded from the result.
193197
MSR_EXCLUSION_LIST
194198
.iter()
195199
.chain(MSR_EXCLUSION_LIST_AMD.iter())
196200
.for_each(|range| {
197201
(range.base..(range.base + range.nmsrs)).for_each(|id| {
198-
map.insert(id, 0);
202+
entries.push(entry(id, 0));
199203
})
200204
});
201-
map
205+
Msrs::from_entries(&entries).unwrap()
202206
}
203207

204208
fn build_expected_msr_modifiers() -> Vec<RegisterModifier> {

src/vmm/src/arch/x86_64/msr.rs

Lines changed: 54 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -423,21 +423,43 @@ pub fn create_boot_msr_entries() -> Vec<kvm_msr_entry> {
423423
]
424424
}
425425

426+
/// Insert or update an MSR entry in `msrs`, mirroring `BTreeMap::insert` semantics: when an entry
427+
/// with the given index already exists its `data` is overwritten, otherwise a new entry is pushed.
428+
///
429+
/// # Errors
430+
///
431+
/// Propagates [`vmm_sys_util::fam::Error`] from [`Msrs::push`] when the FAM struct is at capacity.
432+
pub fn msrs_insert(msrs: &mut Msrs, index: u32, data: u64) {
433+
if let Some(entry) = msrs
434+
.as_mut_slice()
435+
.iter_mut()
436+
.find(|entry| entry.index == index)
437+
{
438+
entry.data = data;
439+
} else {
440+
msrs.push(kvm_msr_entry {
441+
index,
442+
data,
443+
..Default::default()
444+
})
445+
.expect("MSRS entry count exceeded KVM_MAX_MSR_ENTRIES");
446+
}
447+
}
448+
426449
/// Configure Model Specific Registers (MSRs) required to boot Linux for a given x86_64 vCPU.
427450
///
428451
/// # Arguments
429452
///
430453
/// * `vcpu` - Structure for the VCPU that holds the VCPU's fd.
454+
/// * `msrs` - The MSRs to write to KVM.
431455
///
432456
/// # Errors
433457
///
434458
/// When:
435-
/// - Failed to create [`vmm_sys_util::fam::FamStructWrapper`] for MSRs.
436459
/// - [`kvm_ioctls::ioctls::vcpu::VcpuFd::set_msrs`] errors.
437460
/// - [`kvm_ioctls::ioctls::vcpu::VcpuFd::set_msrs`] fails to write all given MSRs entries.
438-
pub fn set_msrs(vcpu: &VcpuFd, msr_entries: &[kvm_msr_entry]) -> Result<(), MsrError> {
439-
let msrs = Msrs::from_entries(msr_entries)?;
440-
vcpu.set_msrs(&msrs)
461+
pub fn set_msrs(vcpu: &VcpuFd, msrs: &Msrs) -> Result<(), MsrError> {
462+
vcpu.set_msrs(msrs)
441463
.map_err(MsrError::SetMsrs)
442464
.and_then(|msrs_written| {
443465
if msrs_written == msrs.as_fam_struct_ref().nmsrs as usize {
@@ -484,7 +506,8 @@ mod tests {
484506
fn test_setup_msrs() {
485507
let vcpu = create_vcpu();
486508
let msr_boot_entries = create_boot_msr_entries();
487-
set_msrs(&vcpu, &msr_boot_entries).unwrap();
509+
let msrs = Msrs::from_entries(&msr_boot_entries).unwrap();
510+
set_msrs(&vcpu, &msrs).unwrap();
488511

489512
// This test will check against the last MSR entry configured (the tenth one).
490513
// See create_msr_entries() for details.
@@ -512,12 +535,31 @@ mod tests {
512535
// Test `set_msrs()` with a valid MSR entry. It should succeed, as IA32_TSC MSR is listed
513536
// in supported MSRs as of now.
514537
let vcpu = create_vcpu();
515-
let msr_entries = vec![kvm_msr_entry {
538+
let msrs = Msrs::from_entries(&[kvm_msr_entry {
516539
index: MSR_IA32_TSC,
517540
data: 0,
518541
..Default::default()
519-
}];
520-
set_msrs(&vcpu, &msr_entries).unwrap();
542+
}])
543+
.unwrap();
544+
set_msrs(&vcpu, &msrs).unwrap();
545+
}
546+
547+
#[test]
548+
fn test_msrs_insert_overwrites_and_pushes() {
549+
let mut msrs = Msrs::new(0).unwrap();
550+
551+
msrs_insert(&mut msrs, 0x10, 0xaa);
552+
msrs_insert(&mut msrs, 0x20, 0xbb);
553+
assert_eq!(msrs.as_slice().len(), 2);
554+
assert_eq!(msrs.as_slice()[0].data, 0xaa);
555+
assert_eq!(msrs.as_slice()[1].data, 0xbb);
556+
557+
msrs_insert(&mut msrs, 0x10, 0xcc);
558+
assert_eq!(msrs.as_slice().len(), 2);
559+
assert_eq!(msrs.as_slice()[0].index, 0x10);
560+
assert_eq!(msrs.as_slice()[0].data, 0xcc);
561+
assert_eq!(msrs.as_slice()[1].index, 0x20);
562+
assert_eq!(msrs.as_slice()[1].data, 0xbb);
521563
}
522564

523565
#[test]
@@ -526,12 +568,13 @@ mod tests {
526568
// listed in supported MSRs as of now. If hardware vendor adds this MSR index and KVM
527569
// supports this MSR, we need to change the index as needed.
528570
let vcpu = create_vcpu();
529-
let msr_entries = vec![kvm_msr_entry {
571+
let msrs = Msrs::from_entries(&[kvm_msr_entry {
530572
index: 2,
531573
..Default::default()
532-
}];
574+
}])
575+
.unwrap();
533576
assert_eq!(
534-
set_msrs(&vcpu, &msr_entries).unwrap_err(),
577+
set_msrs(&vcpu, &msrs).unwrap_err(),
535578
MsrError::SetMsrsIncomplete
536579
);
537580
}

src/vmm/src/arch/x86_64/vcpu.rs

Lines changed: 18 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
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::BTreeMap;
98
use std::fmt::Debug;
109
use std::sync::Arc;
1110

@@ -20,7 +19,7 @@ use vmm_sys_util::fam::{self, FamStruct};
2019
use crate::arch::EntryPoint;
2120
use crate::arch::x86_64::generated::msr_index::{MSR_IA32_TSC, MSR_IA32_TSC_DEADLINE};
2221
use crate::arch::x86_64::interrupts;
23-
use crate::arch::x86_64::msr::{MsrError, create_boot_msr_entries};
22+
use crate::arch::x86_64::msr::{MsrError, create_boot_msr_entries, msrs_insert};
2423
use crate::arch::x86_64::regs::{SetupFpuError, SetupRegistersError, SetupSpecialRegistersError};
2524
use crate::cpu_config::x86_64::{CpuConfiguration, cpuid};
2625
use crate::logger::{IncMetric, METRICS, error, warn};
@@ -222,12 +221,13 @@ impl KvmVcpu {
222221

223222
// Clone MSR entries that are modified by CPU template from `VcpuConfig`.
224223
let mut msrs = vcpu_config.cpu_config.msrs.clone();
225-
self.msrs_to_save.extend(msrs.keys());
224+
self.msrs_to_save
225+
.extend(msrs.as_slice().iter().map(|entry| entry.index));
226226

227227
// Apply MSR modification to comply the linux boot protocol.
228-
create_boot_msr_entries().into_iter().for_each(|entry| {
229-
msrs.insert(entry.index, entry.data);
230-
});
228+
for entry in create_boot_msr_entries() {
229+
msrs_insert(&mut msrs, entry.index, entry.data);
230+
}
231231

232232
// TODO - Add/amend MSRs for vCPUs based on cpu_config
233233
// By this point the Guest CPUID is established. Some CPU features require MSRs
@@ -247,16 +247,7 @@ impl KvmVcpu {
247247
// save is `architectural MSRs` + `MSRs inferred through CPUID` + `other
248248
// MSRs defined by the template`
249249

250-
let kvm_msrs = msrs
251-
.into_iter()
252-
.map(|entry| kvm_bindings::kvm_msr_entry {
253-
index: entry.0,
254-
data: entry.1,
255-
..Default::default()
256-
})
257-
.collect::<Vec<_>>();
258-
259-
crate::arch::x86_64::msr::set_msrs(&self.fd, &kvm_msrs)?;
250+
crate::arch::x86_64::msr::set_msrs(&self.fd, &msrs)?;
260251
crate::arch::x86_64::regs::setup_regs(&self.fd, kernel_entry_point)?;
261252
crate::arch::x86_64::regs::setup_fpu(&self.fd)?;
262253
crate::arch::x86_64::regs::setup_sregs(guest_mem, &self.fd, kernel_entry_point.protocol)?;
@@ -534,19 +525,18 @@ impl KvmVcpu {
534525
/// # Errors
535526
///
536527
/// * When `KvmVcpu::get_msr_chunks()` returns errors.
528+
/// * When [`kvm_bindings::Msrs::new`] returns errors.
537529
pub fn get_msrs(
538530
&self,
539531
msr_index_iter: impl ExactSizeIterator<Item = u32>,
540-
) -> Result<BTreeMap<u32, u64>, KvmVcpuError> {
541-
let mut msrs = BTreeMap::new();
542-
self.get_msr_chunks(msr_index_iter)?
543-
.iter()
544-
.for_each(|msr_chunk| {
545-
msr_chunk.as_slice().iter().for_each(|msr| {
546-
msrs.insert(msr.index, msr.data);
547-
});
548-
});
549-
Ok(msrs)
532+
) -> Result<Msrs, KvmVcpuError> {
533+
let mut combined = Msrs::new(0).map_err(KvmVcpuError::Fam)?;
534+
for chunk in self.get_msr_chunks(msr_index_iter)? {
535+
for entry in chunk.as_slice() {
536+
msrs_insert(&mut combined, entry.index, entry.data);
537+
}
538+
}
539+
Ok(combined)
550540
}
551541

552542
/// Save the KVM internal state.
@@ -1012,7 +1002,7 @@ mod tests {
10121002
smt: false,
10131003
cpu_config: CpuConfiguration {
10141004
cpuid: Cpuid::try_from(vm.kvm().supported_cpuid.clone()).unwrap(),
1015-
msrs: BTreeMap::new(),
1005+
msrs: Msrs::new(0).unwrap(),
10161006
},
10171007
};
10181008
vcpu.configure(
@@ -1080,7 +1070,7 @@ mod tests {
10801070
smt: false,
10811071
cpu_config: CpuConfiguration {
10821072
cpuid: Cpuid::try_from(vm.kvm().supported_cpuid.clone()).unwrap(),
1083-
msrs: BTreeMap::new(),
1073+
msrs: Msrs::new(0).unwrap(),
10841074
},
10851075
};
10861076

0 commit comments

Comments
 (0)