Skip to content

Commit ef7cdfb

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 fa8fc85 commit ef7cdfb

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.6"
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;
@@ -47,10 +46,11 @@ fn cpuid_to_modifiers(cpuid: &Cpuid) -> Vec<CpuidLeafModifier> {
4746
result
4847
}
4948

50-
fn msrs_to_modifier(msrs: &BTreeMap<u32, u64>) -> Vec<RegisterModifier> {
49+
fn msrs_to_modifier(msrs: &Msrs) -> Vec<RegisterModifier> {
5150
let mut msrs: Vec<RegisterModifier> = msrs
51+
.as_slice()
5252
.iter()
53-
.map(|(index, value)| msr_modifier!(*index, *value))
53+
.map(|entry| msr_modifier!(entry.index, entry.data))
5454
.collect();
5555

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

123123
#[cfg(test)]
124124
mod tests {
125-
use std::collections::BTreeMap;
126-
125+
use kvm_bindings::kvm_msr_entry;
127126
use vmm::cpu_config::x86_64::cpuid::IntelCpuid;
128127

129128
use super::*;
@@ -183,24 +182,29 @@ mod tests {
183182
]
184183
}
185184

186-
fn build_sample_msrs() -> BTreeMap<u32, u64> {
187-
let mut map = BTreeMap::from([
185+
fn build_sample_msrs() -> Msrs {
186+
let entry = |index, data| kvm_msr_entry {
187+
index,
188+
data,
189+
..Default::default()
190+
};
191+
let mut entries = vec![
188192
// should be sorted in the result.
189-
(0x1, 0xffff_ffff_ffff_ffff),
190-
(0x5, 0xffff_ffff_0000_0000),
191-
(0x3, 0x0000_0000_ffff_ffff),
192-
(0x2, 0x0000_0000_0000_0000),
193-
]);
193+
entry(0x1, 0xffff_ffff_ffff_ffff),
194+
entry(0x5, 0xffff_ffff_0000_0000),
195+
entry(0x3, 0x0000_0000_ffff_ffff),
196+
entry(0x2, 0x0000_0000_0000_0000),
197+
];
194198
// should be excluded from the result.
195199
MSR_EXCLUSION_LIST
196200
.iter()
197201
.chain(MSR_EXCLUSION_LIST_AMD.iter())
198202
.for_each(|range| {
199203
(range.base..(range.base + range.nmsrs)).for_each(|id| {
200-
map.insert(id, 0);
204+
entries.push(entry(id, 0));
201205
})
202206
});
203-
map
207+
Msrs::from_entries(&entries).unwrap()
204208
}
205209

206210
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
@@ -426,21 +426,43 @@ pub fn create_boot_msr_entries() -> Vec<kvm_msr_entry> {
426426
]
427427
}
428428

429+
/// Insert or update an MSR entry in `msrs`, mirroring `BTreeMap::insert` semantics: when an entry
430+
/// with the given index already exists its `data` is overwritten, otherwise a new entry is pushed.
431+
///
432+
/// # Errors
433+
///
434+
/// Propagates [`vmm_sys_util::fam::Error`] from [`Msrs::push`] when the FAM struct is at capacity.
435+
pub fn msrs_insert(msrs: &mut Msrs, index: u32, data: u64) {
436+
if let Some(entry) = msrs
437+
.as_mut_slice()
438+
.iter_mut()
439+
.find(|entry| entry.index == index)
440+
{
441+
entry.data = data;
442+
} else {
443+
msrs.push(kvm_msr_entry {
444+
index,
445+
data,
446+
..Default::default()
447+
})
448+
.expect("MSRS entry count exceeded KVM_MAX_MSR_ENTRIES");
449+
}
450+
}
451+
429452
/// Configure Model Specific Registers (MSRs) required to boot Linux for a given x86_64 vCPU.
430453
///
431454
/// # Arguments
432455
///
433456
/// * `vcpu` - Structure for the VCPU that holds the VCPU's fd.
457+
/// * `msrs` - The MSRs to write to KVM.
434458
///
435459
/// # Errors
436460
///
437461
/// When:
438-
/// - Failed to create [`vmm_sys_util::fam::FamStructWrapper`] for MSRs.
439462
/// - [`kvm_ioctls::ioctls::vcpu::VcpuFd::set_msrs`] errors.
440463
/// - [`kvm_ioctls::ioctls::vcpu::VcpuFd::set_msrs`] fails to write all given MSRs entries.
441-
pub fn set_msrs(vcpu: &VcpuFd, msr_entries: &[kvm_msr_entry]) -> Result<(), MsrError> {
442-
let msrs = Msrs::from_entries(msr_entries)?;
443-
vcpu.set_msrs(&msrs)
464+
pub fn set_msrs(vcpu: &VcpuFd, msrs: &Msrs) -> Result<(), MsrError> {
465+
vcpu.set_msrs(msrs)
444466
.map_err(MsrError::SetMsrs)
445467
.and_then(|msrs_written| {
446468
if msrs_written == msrs.as_fam_struct_ref().nmsrs as usize {
@@ -487,7 +509,8 @@ mod tests {
487509
fn test_setup_msrs() {
488510
let vcpu = create_vcpu();
489511
let msr_boot_entries = create_boot_msr_entries();
490-
set_msrs(&vcpu, &msr_boot_entries).unwrap();
512+
let msrs = Msrs::from_entries(&msr_boot_entries).unwrap();
513+
set_msrs(&vcpu, &msrs).unwrap();
491514

492515
// This test will check against the last MSR entry configured (the tenth one).
493516
// See create_msr_entries() for details.
@@ -515,12 +538,31 @@ mod tests {
515538
// Test `set_msrs()` with a valid MSR entry. It should succeed, as IA32_TSC MSR is listed
516539
// in supported MSRs as of now.
517540
let vcpu = create_vcpu();
518-
let msr_entries = vec![kvm_msr_entry {
541+
let msrs = Msrs::from_entries(&[kvm_msr_entry {
519542
index: MSR_IA32_TSC,
520543
data: 0,
521544
..Default::default()
522-
}];
523-
set_msrs(&vcpu, &msr_entries).unwrap();
545+
}])
546+
.unwrap();
547+
set_msrs(&vcpu, &msrs).unwrap();
548+
}
549+
550+
#[test]
551+
fn test_msrs_insert_overwrites_and_pushes() {
552+
let mut msrs = Msrs::new(0).unwrap();
553+
554+
msrs_insert(&mut msrs, 0x10, 0xaa);
555+
msrs_insert(&mut msrs, 0x20, 0xbb);
556+
assert_eq!(msrs.as_slice().len(), 2);
557+
assert_eq!(msrs.as_slice()[0].data, 0xaa);
558+
assert_eq!(msrs.as_slice()[1].data, 0xbb);
559+
560+
msrs_insert(&mut msrs, 0x10, 0xcc);
561+
assert_eq!(msrs.as_slice().len(), 2);
562+
assert_eq!(msrs.as_slice()[0].index, 0x10);
563+
assert_eq!(msrs.as_slice()[0].data, 0xcc);
564+
assert_eq!(msrs.as_slice()[1].index, 0x20);
565+
assert_eq!(msrs.as_slice()[1].data, 0xbb);
524566
}
525567

526568
#[test]
@@ -529,12 +571,13 @@ mod tests {
529571
// listed in supported MSRs as of now. If hardware vendor adds this MSR index and KVM
530572
// supports this MSR, we need to change the index as needed.
531573
let vcpu = create_vcpu();
532-
let msr_entries = vec![kvm_msr_entry {
574+
let msrs = Msrs::from_entries(&[kvm_msr_entry {
533575
index: 2,
534576
..Default::default()
535-
}];
577+
}])
578+
.unwrap();
536579
assert_eq!(
537-
set_msrs(&vcpu, &msr_entries).unwrap_err(),
580+
set_msrs(&vcpu, &msrs).unwrap_err(),
538581
MsrError::SetMsrsIncomplete
539582
);
540583
}

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)