Skip to content

Commit 3c5cfdd

Browse files
committed
feat: added configurable vcpu features to cpu templates
Added ability to configure vcpu features from cpu templates. To make code clearer I moved `vcpu.init` out of `create_vcpus` method. Because of this I had to update restoration path to init vcpus earlier. This in turn removed need for `VcupEvent::RestoreState` and `restore_vcpu_states` method. Because this is the first FC release with this feature it is invalid to create snapshots for older versions if any of additional vcpu features are requested, so there is an additional check for this. Signed-off-by: Egor Lazarchuk <[email protected]>
1 parent 97c71fe commit 3c5cfdd

File tree

5 files changed

+117
-123
lines changed

5 files changed

+117
-123
lines changed

src/vmm/src/builder.rs

+30-8
Original file line numberDiff line numberDiff line change
@@ -450,7 +450,7 @@ pub fn build_microvm_from_snapshot(
450450
})?;
451451

452452
// Build Vmm.
453-
let (mut vmm, vcpus) = create_vmm_and_vcpus(
453+
let (mut vmm, mut vcpus) = create_vmm_and_vcpus(
454454
instance_info,
455455
event_manager,
456456
guest_memory.clone(),
@@ -474,13 +474,34 @@ pub fn build_microvm_from_snapshot(
474474
}
475475
}
476476

477+
// Restore vcpus kvm state.
477478
#[cfg(target_arch = "aarch64")]
478479
{
480+
for (i, (vcpu, state)) in vcpus
481+
.iter_mut()
482+
.zip(microvm_state.vcpu_states.iter())
483+
.enumerate()
484+
{
485+
vcpu.kvm_vcpu
486+
.restore_state(vmm.vm.fd(), state, i as u8)
487+
.map_err(crate::vstate::vcpu::VcpuError::VcpuResponse)
488+
.map_err(RestoreVcpusError::RestoreVcpuState)
489+
.map_err(BuildMicrovmFromSnapshotError::RestoreVcpus)?;
490+
}
479491
let mpidrs = construct_kvm_mpidrs(&microvm_state.vcpu_states);
480492
// Restore kvm vm state.
481493
vmm.vm.restore_state(&mpidrs, &microvm_state.vm_state)?;
482494
}
483495

496+
#[cfg(target_arch = "x86_64")]
497+
for (vcpu, state) in vcpus.iter_mut().zip(microvm_state.vcpu_states.iter()) {
498+
vcpu.kvm_vcpu
499+
.restore_state(state)
500+
.map_err(crate::vstate::vcpu::VcpuError::VcpuResponse)
501+
.map_err(RestoreVcpusError::RestoreVcpuState)
502+
.map_err(BuildMicrovmFromSnapshotError::RestoreVcpus)?;
503+
}
504+
484505
// Restore kvm vm state.
485506
#[cfg(target_arch = "x86_64")]
486507
vmm.vm.restore_state(&microvm_state.vm_state)?;
@@ -520,9 +541,6 @@ pub fn build_microvm_from_snapshot(
520541
.clone(),
521542
)?;
522543

523-
// Restore vcpus kvm state.
524-
vmm.restore_vcpu_states(microvm_state.vcpu_states)?;
525-
526544
let vmm = Arc::new(Mutex::new(vmm));
527545
event_manager.add_subscriber(vmm.clone());
528546

@@ -723,11 +741,7 @@ fn create_vcpus(vm: &Vm, vcpu_count: u8, exit_evt: &EventFd) -> Result<Vec<Vcpu>
723741
let mut vcpus = Vec::with_capacity(vcpu_count as usize);
724742
for cpu_idx in 0..vcpu_count {
725743
let exit_evt = exit_evt.try_clone().map_err(VmmError::EventFd)?;
726-
727744
let vcpu = Vcpu::new(cpu_idx, vm, exit_evt).map_err(VmmError::VcpuCreate)?;
728-
#[cfg(target_arch = "aarch64")]
729-
vcpu.kvm_vcpu.init(vm.fd()).map_err(VmmError::VcpuInit)?;
730-
731745
vcpus.push(vcpu);
732746
}
733747
Ok(vcpus)
@@ -764,6 +778,14 @@ pub fn configure_system_for_boot(
764778
let cpu_config = {
765779
use crate::arch::aarch64::regs::Aarch64RegisterVec;
766780
use crate::arch::aarch64::vcpu::get_registers;
781+
782+
for vcpu in vcpus.iter_mut() {
783+
vcpu.kvm_vcpu
784+
.init(vmm.vm.fd(), &cpu_template.vcpu_features)
785+
.map_err(VmmError::VcpuInit)
786+
.map_err(Internal)?;
787+
}
788+
767789
let mut regs = Aarch64RegisterVec::default();
768790
get_registers(&vcpus[0].kvm_vcpu.fd, &cpu_template.reg_list(), &mut regs)
769791
.map_err(GuestConfigError)?;

src/vmm/src/lib.rs

-32
Original file line numberDiff line numberDiff line change
@@ -561,38 +561,6 @@ impl Vmm {
561561
Ok(vcpu_states)
562562
}
563563

564-
/// Restores vcpus kvm states.
565-
pub fn restore_vcpu_states(
566-
&mut self,
567-
mut vcpu_states: Vec<VcpuState>,
568-
) -> Result<(), RestoreVcpusError> {
569-
if vcpu_states.len() != self.vcpus_handles.len() {
570-
return Err(RestoreVcpusError::InvalidInput);
571-
}
572-
for (handle, state) in self.vcpus_handles.iter().zip(vcpu_states.drain(..)) {
573-
handle.send_event(VcpuEvent::RestoreState(Box::new(state)))?;
574-
}
575-
576-
let vcpu_responses = self
577-
.vcpus_handles
578-
.iter()
579-
// `Iterator::collect` can transform a `Vec<Result>` into a `Result<Vec>`.
580-
.map(|handle| handle.response_receiver().recv_timeout(RECV_TIMEOUT_SEC))
581-
.collect::<Result<Vec<VcpuResponse>, RecvTimeoutError>>()
582-
.map_err(|_| RestoreVcpusError::UnexpectedVcpuResponse)?;
583-
584-
for response in vcpu_responses.into_iter() {
585-
match response {
586-
VcpuResponse::RestoredState => Ok(()),
587-
VcpuResponse::Error(err) => Err(RestoreVcpusError::RestoreVcpuState(err)),
588-
VcpuResponse::NotAllowed(reason) => Err(RestoreVcpusError::NotAllowed(reason)),
589-
_ => Err(RestoreVcpusError::UnexpectedVcpuResponse),
590-
}?;
591-
}
592-
593-
Ok(())
594-
}
595-
596564
/// Dumps CPU configuration.
597565
pub fn dump_cpu_config(&mut self) -> Result<Vec<CpuConfiguration>, DumpCpuConfigError> {
598566
for handle in self.vcpus_handles.iter() {

src/vmm/src/persist.rs

+6
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,12 @@ pub fn additional_snapshot_version_check(
370370
{
371371
return Err(CreateSnapshotError::UnsupportedVersion);
372372
}
373+
374+
// We forbid snapshots older then 1.5 if any additional vcpu features are requested
375+
#[cfg(target_arch = "aarch64")]
376+
if microvm_state.vcpu_states[0].kvi.is_some() && version < FC_V1_5_SNAP_VERSION {
377+
return Err(CreateSnapshotError::UnsupportedVersion);
378+
}
373379
Ok(())
374380
}
375381

src/vmm/src/vstate/vcpu/aarch64.rs

+66-40
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ use crate::arch::aarch64::vcpu::{
1818
get_all_registers, get_all_registers_ids, get_mpidr, get_mpstate, get_registers, set_mpstate,
1919
set_registers, setup_boot_regs, VcpuError as ArchError,
2020
};
21+
use crate::cpu_config::aarch64::custom_cpu_template::VcpuFeatures;
2122
use crate::cpu_config::templates::CpuConfiguration;
2223
use crate::vcpu::{VcpuConfig, VcpuError};
2324
use crate::vstate::vcpu::VcpuEmulation;
@@ -40,6 +41,8 @@ pub enum KvmVcpuError {
4041
ApplyCpuTemplate(ArchError),
4142
#[error("Failed to restore the state of the vcpu: {0}")]
4243
RestoreState(ArchError),
44+
#[error("Failed to restore the state of the vcpu: no kvi provided")]
45+
MissingKvi,
4346
#[error("Failed to save the state of the vcpu: {0}")]
4447
SaveState(ArchError),
4548
}
@@ -53,6 +56,7 @@ pub struct KvmVcpu {
5356
pub fd: VcpuFd,
5457
pub mmio_bus: Option<crate::devices::Bus>,
5558
mpidr: u64,
59+
kvi: Option<kvm_bindings::kvm_vcpu_init>,
5660
}
5761

5862
impl KvmVcpu {
@@ -73,6 +77,7 @@ impl KvmVcpu {
7377
fd: kvm_vcpu,
7478
mmio_bus: None,
7579
mpidr: 0,
80+
kvi: None,
7681
})
7782
}
7883

@@ -118,20 +123,43 @@ impl KvmVcpu {
118123
/// # Arguments
119124
///
120125
/// * `vm_fd` - The kvm `VmFd` for this microvm.
121-
pub fn init(&self, vm_fd: &VmFd) -> Result<(), KvmVcpuError> {
122-
let mut kvi: kvm_bindings::kvm_vcpu_init = kvm_bindings::kvm_vcpu_init::default();
126+
pub fn init(
127+
&mut self,
128+
vm_fd: &VmFd,
129+
vcpu_features: &[VcpuFeatures],
130+
) -> Result<(), KvmVcpuError> {
131+
let mut kvi = Self::default_kvi(vm_fd, self.index)?;
132+
133+
for feature in vcpu_features.iter() {
134+
kvi.features[feature.index as usize] |= feature.bitmap;
135+
}
123136

137+
self.kvi = if !vcpu_features.is_empty() {
138+
Some(kvi)
139+
} else {
140+
None
141+
};
142+
self.fd.vcpu_init(&kvi).map_err(KvmVcpuError::Init)
143+
}
144+
145+
pub fn default_kvi(
146+
vm_fd: &VmFd,
147+
index: u8,
148+
) -> Result<kvm_bindings::kvm_vcpu_init, KvmVcpuError> {
149+
let mut kvi: kvm_bindings::kvm_vcpu_init = kvm_bindings::kvm_vcpu_init::default();
124150
// This reads back the kernel's preferred target type.
125151
vm_fd
126152
.get_preferred_target(&mut kvi)
127153
.map_err(KvmVcpuError::GetPreferredTarget)?;
128154
// We already checked that the capability is supported.
129155
kvi.features[0] |= 1 << kvm_bindings::KVM_ARM_VCPU_PSCI_0_2;
156+
130157
// Non-boot cpus are powered off initially.
131-
if self.index > 0 {
158+
if index > 0 {
132159
kvi.features[0] |= 1 << kvm_bindings::KVM_ARM_VCPU_POWER_OFF;
133160
}
134-
self.fd.vcpu_init(&kvi).map_err(KvmVcpuError::Init)
161+
162+
Ok(kvi)
135163
}
136164

137165
/// Save the KVM internal state.
@@ -142,11 +170,23 @@ impl KvmVcpu {
142170
};
143171
get_all_registers(&self.fd, &mut state.regs).map_err(KvmVcpuError::SaveState)?;
144172
state.mpidr = get_mpidr(&self.fd).map_err(KvmVcpuError::SaveState)?;
173+
state.kvi = self.kvi;
145174
Ok(state)
146175
}
147176

148177
/// Use provided state to populate KVM internal state.
149-
pub fn restore_state(&self, state: &VcpuState) -> Result<(), KvmVcpuError> {
178+
pub fn restore_state(
179+
&mut self,
180+
vm_fd: &VmFd,
181+
state: &VcpuState,
182+
index: u8,
183+
) -> Result<(), KvmVcpuError> {
184+
let kvi = match state.kvi {
185+
Some(kvi) => kvi,
186+
None => Self::default_kvi(vm_fd, index)?,
187+
};
188+
self.fd.vcpu_init(&kvi).map_err(KvmVcpuError::Init)?;
189+
self.kvi = state.kvi;
150190
set_registers(&self.fd, &state.regs).map_err(KvmVcpuError::RestoreState)?;
151191
set_mpstate(&self.fd, state.mp_state).map_err(KvmVcpuError::RestoreState)?;
152192
Ok(())
@@ -192,13 +232,19 @@ pub struct VcpuState {
192232
// The VmState will give this away for saving restoring the icc and redistributor
193233
// registers.
194234
pub mpidr: u64,
235+
#[version(start = 2, default_fn = "default_kvi")]
236+
pub kvi: Option<kvm_bindings::kvm_vcpu_init>,
195237
}
196238

197239
impl VcpuState {
198240
fn default_old_regs(_: u16) -> Vec<Aarch64RegisterOld> {
199241
Vec::default()
200242
}
201243

244+
fn default_kvi(_: u16) -> Option<kvm_bindings::kvm_vcpu_init> {
245+
None
246+
}
247+
202248
fn de_regs(&mut self, _source_version: u16) -> VersionizeResult<()> {
203249
let mut regs = Aarch64RegisterVec::default();
204250
for reg in self.old_regs.iter() {
@@ -227,31 +273,23 @@ mod tests {
227273
#![allow(clippy::undocumented_unsafe_blocks)]
228274
use std::os::unix::io::AsRawFd;
229275

230-
use kvm_bindings::KVM_REG_SIZE_U64;
231276
use utils::vm_memory::GuestMemoryMmap;
232277

233278
use super::*;
234-
use crate::arch::aarch64::regs::Aarch64RegisterRef;
235279
use crate::cpu_config::aarch64::CpuConfiguration;
236280
use crate::vcpu::VcpuConfig;
237281
use crate::vstate::vm::tests::setup_vm;
238282
use crate::vstate::vm::Vm;
239283

240284
fn setup_vcpu(mem_size: usize) -> (Vm, KvmVcpu, GuestMemoryMmap) {
241285
let (mut vm, vm_mem) = setup_vm(mem_size);
242-
let vcpu = KvmVcpu::new(0, &vm).unwrap();
243-
vcpu.init(vm.fd()).unwrap();
286+
let mut vcpu = KvmVcpu::new(0, &vm).unwrap();
287+
vcpu.init(vm.fd(), &[]).unwrap();
244288
vm.setup_irqchip(1).unwrap();
245289

246290
(vm, vcpu, vm_mem)
247291
}
248292

249-
fn init_vcpu(vcpu: &VcpuFd, vm: &VmFd) {
250-
let mut kvi = kvm_bindings::kvm_vcpu_init::default();
251-
vm.get_preferred_target(&mut kvi).unwrap();
252-
vcpu.vcpu_init(&kvi).unwrap();
253-
}
254-
255293
#[test]
256294
fn test_create_vcpu() {
257295
let (vm, _) = setup_vm(0x1000);
@@ -302,9 +340,9 @@ mod tests {
302340

303341
#[test]
304342
fn test_faulty_init_vcpu() {
305-
let (vm, vcpu, _) = setup_vcpu(0x10000);
343+
let (vm, mut vcpu, _) = setup_vcpu(0x10000);
306344
unsafe { libc::close(vm.fd().as_raw_fd()) };
307-
let err = vcpu.init(vm.fd());
345+
let err = vcpu.init(vm.fd(), &[]);
308346
assert!(err.is_err());
309347
assert_eq!(
310348
err.err().unwrap().to_string(),
@@ -315,7 +353,7 @@ mod tests {
315353
#[test]
316354
fn test_vcpu_save_restore_state() {
317355
let (mut vm, _vm_mem) = setup_vm(0x1000);
318-
let vcpu = KvmVcpu::new(0, &vm).unwrap();
356+
let mut vcpu = KvmVcpu::new(0, &vm).unwrap();
319357
vm.setup_irqchip(1).unwrap();
320358

321359
// Calling KVM_GET_REGLIST before KVM_VCPU_INIT will result in error.
@@ -327,26 +365,14 @@ mod tests {
327365
));
328366

329367
// Try to restore the register using a faulty state.
330-
let mut regs = Aarch64RegisterVec::default();
331-
let mut reg = Aarch64RegisterRef::new(KVM_REG_SIZE_U64, &[0; 8]);
332-
reg.id = 0;
333-
regs.push(reg);
334-
let faulty_vcpu_state = VcpuState {
335-
regs,
336-
..Default::default()
337-
};
338-
339-
let res = vcpu.restore_state(&faulty_vcpu_state);
368+
let faulty_vcpu_state = VcpuState::default();
369+
let res = vcpu.restore_state(vm.fd(), &faulty_vcpu_state, 0);
340370
assert!(res.is_err());
341-
assert!(matches!(
342-
res.unwrap_err(),
343-
KvmVcpuError::RestoreState(ArchError::SetOneReg(0, _))
344-
));
345371

346-
init_vcpu(&vcpu.fd, vm.fd());
372+
vcpu.init(vm.fd(), &[]).unwrap();
347373
let state = vcpu.save_state().expect("Cannot save state of vcpu");
348374
assert!(!state.regs.is_empty());
349-
vcpu.restore_state(&state)
375+
vcpu.restore_state(vm.fd(), &state, 0)
350376
.expect("Cannot restore state of vcpu");
351377
}
352378

@@ -367,20 +393,20 @@ mod tests {
367393
fn test_dump_cpu_config_after_init() {
368394
// Test `dump_cpu_config()` after `KVM_VCPU_INIT`.
369395
let (mut vm, _vm_mem) = setup_vm(0x1000);
370-
let vcpu = KvmVcpu::new(0, &vm).unwrap();
396+
let mut vcpu = KvmVcpu::new(0, &vm).unwrap();
371397
vm.setup_irqchip(1).unwrap();
372-
vcpu.init(vm.fd()).unwrap();
398+
vcpu.init(vm.fd(), &[]).unwrap();
373399

374400
assert!(vcpu.dump_cpu_config().is_ok());
375401
}
376402

377403
#[test]
378404
fn test_setup_non_boot_vcpu() {
379405
let (vm, _) = setup_vm(0x1000);
380-
let vcpu1 = KvmVcpu::new(0, &vm).unwrap();
381-
assert!(vcpu1.init(vm.fd()).is_ok());
382-
let vcpu2 = KvmVcpu::new(1, &vm).unwrap();
383-
assert!(vcpu2.init(vm.fd()).is_ok());
406+
let mut vcpu1 = KvmVcpu::new(0, &vm).unwrap();
407+
assert!(vcpu1.init(vm.fd(), &[]).is_ok());
408+
let mut vcpu2 = KvmVcpu::new(1, &vm).unwrap();
409+
assert!(vcpu2.init(vm.fd(), &[]).is_ok());
384410
}
385411

386412
#[test]

0 commit comments

Comments
 (0)