Skip to content

Commit

Permalink
Add KVM_SET_TSC_KHZ KVM_GET_TSC_KHZ for x86
Browse files Browse the repository at this point in the history
The KVM_GET_TSC_KHZ and KVM_SET_TSC_KHZ allows users to access and
control the frequency of the Time Stamp Counter. This is useful in
cases such as snapshotting, where resuming from a snapshot on a
different CPU than on the original machine will lead to inconsitencies
in applications which make use of the Time Stamp Counter.

This commit:
- implements vCPU wrappers: set_tsc_khz(), get_tsc_khz()
- adds unit tests
- tests for expected failures if KVM_CAP_TSC_CONTROL or
KVM_CAP_GET_TSC_KHZ are missing

Signed-off-by: Andrei Sandu <sandreim@amazon.com>
Signed-off-by: George Pisaltu <gpl@amazon.com>
  • Loading branch information
sandreim authored and andreeaflorescu committed May 13, 2021
1 parent 9d0fd1f commit 94cb91d
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 2 deletions.
2 changes: 1 addition & 1 deletion coverage_config_x86_64.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"coverage_score": 91.2,
"coverage_score": 91.4,
"exclude_path": "",
"crate_features": ""
}
90 changes: 89 additions & 1 deletion src/ioctls/vcpu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use kvm_ioctls::*;
use vmm_sys_util::errno;
use vmm_sys_util::ioctl::{ioctl, ioctl_with_mut_ref, ioctl_with_ref};
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
use vmm_sys_util::ioctl::{ioctl_with_mut_ptr, ioctl_with_ptr};
use vmm_sys_util::ioctl::{ioctl_with_mut_ptr, ioctl_with_ptr, ioctl_with_val};

/// Reasons for vCPU exits.
///
Expand Down Expand Up @@ -1339,6 +1339,61 @@ impl VcpuFd {
let kvm_run = self.kvm_run_ptr.as_mut_ref();
kvm_run.immediate_exit = val;
}

/// Returns the vCPU TSC frequency in KHz or an error if the host has unstable TSC.
///
/// # Example
///
/// ```rust
/// # extern crate kvm_ioctls;
/// # use kvm_ioctls::Kvm;
/// let kvm = Kvm::new().unwrap();
/// let vm = kvm.create_vm().unwrap();
/// let vcpu = vm.create_vcpu(0).unwrap();
/// let tsc_khz = vcpu.get_tsc_khz().unwrap();
/// ```
///
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
pub fn get_tsc_khz(&self) -> Result<u32> {
// Safe because we know that our file is a KVM fd and that the request is one of the ones
// defined by kernel.
let ret = unsafe { ioctl(self, KVM_GET_TSC_KHZ()) };
if ret >= 0 {
Ok(ret as u32)
} else {
Err(errno::Error::new(ret))
}
}

/// Sets the specified vCPU TSC frequency.
///
/// # Arguments
///
/// * `freq` - The frequency unit is KHz as per the the KVM API documentation
/// for `KVM_SET_TSC_KHZ`.
///
/// # Example
///
/// ```rust
/// # extern crate kvm_ioctls;
/// # use kvm_ioctls::Kvm;
/// let kvm = Kvm::new().unwrap();
/// let vm = kvm.create_vm().unwrap();
/// let vcpu = vm.create_vcpu(0).unwrap();
/// vcpu.set_tsc_khz(1000).unwrap();
/// ```
///
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
pub fn set_tsc_khz(&self, freq: u32) -> Result<()> {
// Safe because we know that our file is a KVM fd and that the request is one of the ones
// defined by kernel.
let ret = unsafe { ioctl_with_val(self, KVM_SET_TSC_KHZ(), freq as u64) };
if ret < 0 {
Err(errno::Error::last())
} else {
Ok(())
}
}
}

/// Helper function to create a new `VcpuFd`.
Expand Down Expand Up @@ -2043,6 +2098,8 @@ mod tests {
faulty_vcpu_fd.kvmclock_ctrl().unwrap_err().errno(),
badf_errno
);
assert!(faulty_vcpu_fd.get_tsc_khz().is_err());
assert!(faulty_vcpu_fd.set_tsc_khz(1000000).is_err());
}

#[test]
Expand Down Expand Up @@ -2172,4 +2229,35 @@ mod tests {
vcpu.enable_cap(&cap).unwrap();
}
}
#[cfg(target_arch = "x86_64")]
#[test]
fn test_get_tsc_khz() {
let kvm = Kvm::new().unwrap();
let vm = kvm.create_vm().unwrap();
let vcpu = vm.create_vcpu(0).unwrap();

if !kvm.check_extension(Cap::GetTscKhz) {
assert!(vcpu.get_tsc_khz().is_err())
} else {
assert!(vcpu.get_tsc_khz().unwrap() > 0);
}
}

#[cfg(target_arch = "x86_64")]
#[test]
fn test_set_tsc_khz() {
let kvm = Kvm::new().unwrap();
let vm = kvm.create_vm().unwrap();
let vcpu = vm.create_vcpu(0).unwrap();
let freq = vcpu.get_tsc_khz().unwrap();

if !(kvm.check_extension(Cap::GetTscKhz) && kvm.check_extension(Cap::TscControl)) {
assert!(vcpu.set_tsc_khz(0).is_err());
} else {
assert!(vcpu.set_tsc_khz(freq - 500000).is_ok());
assert_eq!(vcpu.get_tsc_khz().unwrap(), freq - 500000);
assert!(vcpu.set_tsc_khz(freq + 500000).is_ok());
assert_eq!(vcpu.get_tsc_khz().unwrap(), freq + 500000);
}
}
}
7 changes: 7 additions & 0 deletions src/kvm_ioctls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,13 @@ ioctl_iow_nr!(KVM_SET_XCRS, KVMIO, 0xa7, kvm_xcrs);
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
ioctl_io_nr!(KVM_KVMCLOCK_CTRL, KVMIO, 0xad);

/* Available with KVM_CAP_TSC_CONTROL */
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
ioctl_io_nr!(KVM_SET_TSC_KHZ, KVMIO, 0xa2);
/* Available with KVM_CAP_GET_TSC_KHZ */
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
ioctl_io_nr!(KVM_GET_TSC_KHZ, KVMIO, 0xa3);

/* Available with KVM_CAP_ENABLE_CAP */
#[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))]
ioctl_iow_nr!(KVM_ENABLE_CAP, KVMIO, 0xa3, kvm_enable_cap);
Expand Down

0 comments on commit 94cb91d

Please sign in to comment.