Skip to content

Commit 828fa09

Browse files
DakshinDroypat
authored andcommitted
feat: Add PVTime support for ARM
Adds functionality for pvtime, which displays steal time to guest on ARM machines. PVTime is persisted across snapshots as well (snapshot ver updated). - Added ipa per vCPU for mem region storing steal time info. - Persists this ipa per vCPU across snapshots. - Shared steal time mem region is setup on boot and restore from snapshot in builder.rs. Signed-off-by: Dakshin Devanand <dakshind2005@gmail.com>
1 parent ae078ee commit 828fa09

File tree

3 files changed

+110
-1
lines changed

3 files changed

+110
-1
lines changed

src/vmm/src/arch/aarch64/vcpu.rs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use std::mem::offset_of;
1111
use kvm_bindings::*;
1212
use kvm_ioctls::{VcpuExit, VcpuFd, VmFd};
1313
use serde::{Deserialize, Serialize};
14+
use vm_memory::GuestAddress;
1415

1516
use super::get_fdt_addr;
1617
use super::regs::*;
@@ -42,6 +43,8 @@ pub enum VcpuArchError {
4243
Fam(vmm_sys_util::fam::Error),
4344
/// {0}
4445
GetMidrEl1(String),
46+
/// Failed to set/get device attributes for vCPU: {0}
47+
DeviceAttribute(kvm_ioctls::Error),
4548
}
4649

4750
/// Extract the Manufacturer ID from the host.
@@ -115,6 +118,8 @@ pub struct KvmVcpu {
115118
/// Vcpu peripherals, such as buses
116119
pub peripherals: Peripherals,
117120
kvi: kvm_vcpu_init,
121+
/// IPA of steal_time region
122+
pub pvtime_ipa: Option<GuestAddress>,
118123
}
119124

120125
/// Vcpu peripherals
@@ -148,6 +153,7 @@ impl KvmVcpu {
148153
fd: kvm_vcpu,
149154
peripherals: Default::default(),
150155
kvi,
156+
pvtime_ipa: None,
151157
})
152158
}
153159

@@ -243,6 +249,8 @@ impl KvmVcpu {
243249
// the boot state and turned secondary vcpus on.
244250
state.kvi.features[0] &= !(1 << KVM_ARM_VCPU_POWER_OFF);
245251

252+
state.pvtime_ipa = self.pvtime_ipa.map(|guest_addr| guest_addr.0);
253+
246254
Ok(state)
247255
}
248256

@@ -276,6 +284,13 @@ impl KvmVcpu {
276284
}
277285
self.set_mpstate(state.mp_state)
278286
.map_err(KvmVcpuError::RestoreState)?;
287+
288+
// Assumes that steal time memory region was set up already
289+
if let Some(pvtime_ipa) = state.pvtime_ipa {
290+
self.enable_pvtime(GuestAddress(pvtime_ipa))
291+
.map_err(KvmVcpuError::RestoreState)?;
292+
}
293+
279294
Ok(())
280295
}
281296

@@ -439,6 +454,38 @@ impl KvmVcpu {
439454
pub fn set_mpstate(&self, state: kvm_mp_state) -> Result<(), VcpuArchError> {
440455
self.fd.set_mp_state(state).map_err(VcpuArchError::SetMp)
441456
}
457+
458+
/// Check if pvtime (steal time on ARM) is supported for vcpu
459+
pub fn supports_pvtime(&self) -> bool {
460+
let pvtime_device_attr = kvm_bindings::kvm_device_attr {
461+
group: kvm_bindings::KVM_ARM_VCPU_PVTIME_CTRL,
462+
attr: kvm_bindings::KVM_ARM_VCPU_PVTIME_IPA as u64,
463+
addr: 0,
464+
flags: 0,
465+
};
466+
467+
// Use kvm_has_device_attr to check if PVTime is supported
468+
self.fd.has_device_attr(&pvtime_device_attr).is_ok()
469+
}
470+
471+
/// Enables pvtime for vcpu
472+
pub fn enable_pvtime(&mut self, ipa: GuestAddress) -> Result<(), VcpuArchError> {
473+
self.pvtime_ipa = Some(ipa);
474+
475+
// Use KVM syscall (kvm_set_device_attr) to register the vCPU with the steal_time region
476+
let vcpu_device_attr = kvm_bindings::kvm_device_attr {
477+
group: KVM_ARM_VCPU_PVTIME_CTRL,
478+
attr: KVM_ARM_VCPU_PVTIME_IPA as u64,
479+
addr: &ipa.0 as *const u64 as u64, // userspace address of attr data
480+
flags: 0,
481+
};
482+
483+
self.fd
484+
.set_device_attr(&vcpu_device_attr)
485+
.map_err(VcpuArchError::DeviceAttribute)?;
486+
487+
Ok(())
488+
}
442489
}
443490

444491
impl Peripherals {
@@ -467,6 +514,8 @@ pub struct VcpuState {
467514
pub mpidr: u64,
468515
/// kvi states for vcpu initialization.
469516
pub kvi: kvm_vcpu_init,
517+
/// ipa for steal_time region
518+
pub pvtime_ipa: Option<u64>,
470519
}
471520

472521
impl Debug for VcpuState {

src/vmm/src/builder.rs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ use linux_loader::cmdline::Cmdline as LoaderKernelCmdline;
1515
use userfaultfd::Uffd;
1616
use utils::time::TimestampUs;
1717
#[cfg(target_arch = "aarch64")]
18+
use vm_memory::GuestAddress;
19+
#[cfg(target_arch = "aarch64")]
1820
use vm_superio::Rtc;
1921
use vm_superio::Serial;
2022
use vmm_sys_util::eventfd::EventFd;
@@ -82,6 +84,9 @@ pub enum StartMicrovmError {
8284
CreateLegacyDevice(device_manager::legacy::LegacyDeviceError),
8385
/// Error creating VMGenID device: {0}
8486
CreateVMGenID(VmGenIdError),
87+
/// Error enabling pvtime on vcpu: {0}
88+
#[cfg(target_arch = "aarch64")]
89+
EnablePVTime(crate::arch::VcpuArchError),
8590
/// Invalid Memory Configuration: {0}
8691
GuestMemory(crate::vstate::memory::MemoryError),
8792
/// Error with initrd initialization: {0}.
@@ -289,6 +294,13 @@ pub fn build_microvm_for_boot(
289294

290295
attach_vmgenid_device(&mut vmm)?;
291296

297+
#[cfg(target_arch = "aarch64")]
298+
if vcpus[0].kvm_vcpu.supports_pvtime() {
299+
setup_pvtime(&mut vmm, &mut vcpus)?;
300+
} else {
301+
log::warn!("Vcpus do not support pvtime, steal time will not be reported to guest");
302+
}
303+
292304
configure_system_for_boot(
293305
&mut vmm,
294306
vcpus.as_mut(),
@@ -449,6 +461,16 @@ pub fn build_microvm_from_snapshot(
449461
}
450462
}
451463

464+
// Restore allocator state
465+
#[cfg(target_arch = "aarch64")]
466+
if let Some(pvtime_ipa) = vcpus[0].kvm_vcpu.pvtime_ipa {
467+
allocate_pvtime_region(
468+
&mut vmm,
469+
vcpus.len(),
470+
vm_allocator::AllocPolicy::ExactMatch(pvtime_ipa.0),
471+
)?;
472+
}
473+
452474
// Restore vcpus kvm state.
453475
for (vcpu, state) in vcpus.iter_mut().zip(microvm_state.vcpu_states.iter()) {
454476
vcpu.kvm_vcpu
@@ -552,6 +574,44 @@ pub fn setup_serial_device(
552574
Ok(serial)
553575
}
554576

577+
/// 64 bytes due to alignment requirement in 3.1 of https://www.kernel.org/doc/html/v5.8/virt/kvm/devices/vcpu.html#attribute-kvm-arm-vcpu-pvtime-ipa
578+
#[cfg(target_arch = "aarch64")]
579+
const STEALTIME_STRUCT_MEM_SIZE: u64 = 64;
580+
581+
/// Helper method to allocate steal time region
582+
#[cfg(target_arch = "aarch64")]
583+
fn allocate_pvtime_region(
584+
vmm: &mut Vmm,
585+
vcpu_count: usize,
586+
policy: vm_allocator::AllocPolicy,
587+
) -> Result<GuestAddress, StartMicrovmError> {
588+
let size = STEALTIME_STRUCT_MEM_SIZE * vcpu_count as u64;
589+
let addr = vmm
590+
.resource_allocator
591+
.allocate_system_memory(size, STEALTIME_STRUCT_MEM_SIZE, policy)
592+
.map_err(StartMicrovmError::AllocateResources)?;
593+
Ok(GuestAddress(addr))
594+
}
595+
596+
/// Sets up pvtime for all vcpus
597+
#[cfg(target_arch = "aarch64")]
598+
fn setup_pvtime(vmm: &mut Vmm, vcpus: &mut [Vcpu]) -> Result<(), StartMicrovmError> {
599+
// Alloc sys mem for steal time region
600+
let pvtime_mem: GuestAddress =
601+
allocate_pvtime_region(vmm, vcpus.len(), vm_allocator::AllocPolicy::LastMatch)?;
602+
603+
// Register all vcpus with pvtime device
604+
for (i, vcpu) in vcpus.iter_mut().enumerate() {
605+
vcpu.kvm_vcpu
606+
.enable_pvtime(GuestAddress(
607+
pvtime_mem.0 + i as u64 * STEALTIME_STRUCT_MEM_SIZE,
608+
))
609+
.map_err(StartMicrovmError::EnablePVTime)?;
610+
}
611+
612+
Ok(())
613+
}
614+
555615
#[cfg(target_arch = "aarch64")]
556616
fn attach_legacy_devices_aarch64(
557617
event_manager: &mut EventManager,

src/vmm/src/persist.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ pub enum CreateSnapshotError {
148148
}
149149

150150
/// Snapshot version
151-
pub const SNAPSHOT_VERSION: Version = Version::new(6, 0, 0);
151+
pub const SNAPSHOT_VERSION: Version = Version::new(7, 0, 0);
152152

153153
/// Creates a Microvm snapshot.
154154
pub fn create_snapshot(

0 commit comments

Comments
 (0)