Skip to content

Commit 94cb91d

Browse files
sandreimandreeaflorescu
authored andcommitted
Add KVM_SET_TSC_KHZ KVM_GET_TSC_KHZ for x86
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>
1 parent 9d0fd1f commit 94cb91d

File tree

3 files changed

+97
-2
lines changed

3 files changed

+97
-2
lines changed

coverage_config_x86_64.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"coverage_score": 91.2,
2+
"coverage_score": 91.4,
33
"exclude_path": "",
44
"crate_features": ""
55
}

src/ioctls/vcpu.rs

Lines changed: 89 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use kvm_ioctls::*;
1717
use vmm_sys_util::errno;
1818
use vmm_sys_util::ioctl::{ioctl, ioctl_with_mut_ref, ioctl_with_ref};
1919
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
20-
use vmm_sys_util::ioctl::{ioctl_with_mut_ptr, ioctl_with_ptr};
20+
use vmm_sys_util::ioctl::{ioctl_with_mut_ptr, ioctl_with_ptr, ioctl_with_val};
2121

2222
/// Reasons for vCPU exits.
2323
///
@@ -1339,6 +1339,61 @@ impl VcpuFd {
13391339
let kvm_run = self.kvm_run_ptr.as_mut_ref();
13401340
kvm_run.immediate_exit = val;
13411341
}
1342+
1343+
/// Returns the vCPU TSC frequency in KHz or an error if the host has unstable TSC.
1344+
///
1345+
/// # Example
1346+
///
1347+
/// ```rust
1348+
/// # extern crate kvm_ioctls;
1349+
/// # use kvm_ioctls::Kvm;
1350+
/// let kvm = Kvm::new().unwrap();
1351+
/// let vm = kvm.create_vm().unwrap();
1352+
/// let vcpu = vm.create_vcpu(0).unwrap();
1353+
/// let tsc_khz = vcpu.get_tsc_khz().unwrap();
1354+
/// ```
1355+
///
1356+
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
1357+
pub fn get_tsc_khz(&self) -> Result<u32> {
1358+
// Safe because we know that our file is a KVM fd and that the request is one of the ones
1359+
// defined by kernel.
1360+
let ret = unsafe { ioctl(self, KVM_GET_TSC_KHZ()) };
1361+
if ret >= 0 {
1362+
Ok(ret as u32)
1363+
} else {
1364+
Err(errno::Error::new(ret))
1365+
}
1366+
}
1367+
1368+
/// Sets the specified vCPU TSC frequency.
1369+
///
1370+
/// # Arguments
1371+
///
1372+
/// * `freq` - The frequency unit is KHz as per the the KVM API documentation
1373+
/// for `KVM_SET_TSC_KHZ`.
1374+
///
1375+
/// # Example
1376+
///
1377+
/// ```rust
1378+
/// # extern crate kvm_ioctls;
1379+
/// # use kvm_ioctls::Kvm;
1380+
/// let kvm = Kvm::new().unwrap();
1381+
/// let vm = kvm.create_vm().unwrap();
1382+
/// let vcpu = vm.create_vcpu(0).unwrap();
1383+
/// vcpu.set_tsc_khz(1000).unwrap();
1384+
/// ```
1385+
///
1386+
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
1387+
pub fn set_tsc_khz(&self, freq: u32) -> Result<()> {
1388+
// Safe because we know that our file is a KVM fd and that the request is one of the ones
1389+
// defined by kernel.
1390+
let ret = unsafe { ioctl_with_val(self, KVM_SET_TSC_KHZ(), freq as u64) };
1391+
if ret < 0 {
1392+
Err(errno::Error::last())
1393+
} else {
1394+
Ok(())
1395+
}
1396+
}
13421397
}
13431398

13441399
/// Helper function to create a new `VcpuFd`.
@@ -2043,6 +2098,8 @@ mod tests {
20432098
faulty_vcpu_fd.kvmclock_ctrl().unwrap_err().errno(),
20442099
badf_errno
20452100
);
2101+
assert!(faulty_vcpu_fd.get_tsc_khz().is_err());
2102+
assert!(faulty_vcpu_fd.set_tsc_khz(1000000).is_err());
20462103
}
20472104

20482105
#[test]
@@ -2172,4 +2229,35 @@ mod tests {
21722229
vcpu.enable_cap(&cap).unwrap();
21732230
}
21742231
}
2232+
#[cfg(target_arch = "x86_64")]
2233+
#[test]
2234+
fn test_get_tsc_khz() {
2235+
let kvm = Kvm::new().unwrap();
2236+
let vm = kvm.create_vm().unwrap();
2237+
let vcpu = vm.create_vcpu(0).unwrap();
2238+
2239+
if !kvm.check_extension(Cap::GetTscKhz) {
2240+
assert!(vcpu.get_tsc_khz().is_err())
2241+
} else {
2242+
assert!(vcpu.get_tsc_khz().unwrap() > 0);
2243+
}
2244+
}
2245+
2246+
#[cfg(target_arch = "x86_64")]
2247+
#[test]
2248+
fn test_set_tsc_khz() {
2249+
let kvm = Kvm::new().unwrap();
2250+
let vm = kvm.create_vm().unwrap();
2251+
let vcpu = vm.create_vcpu(0).unwrap();
2252+
let freq = vcpu.get_tsc_khz().unwrap();
2253+
2254+
if !(kvm.check_extension(Cap::GetTscKhz) && kvm.check_extension(Cap::TscControl)) {
2255+
assert!(vcpu.set_tsc_khz(0).is_err());
2256+
} else {
2257+
assert!(vcpu.set_tsc_khz(freq - 500000).is_ok());
2258+
assert_eq!(vcpu.get_tsc_khz().unwrap(), freq - 500000);
2259+
assert!(vcpu.set_tsc_khz(freq + 500000).is_ok());
2260+
assert_eq!(vcpu.get_tsc_khz().unwrap(), freq + 500000);
2261+
}
2262+
}
21752263
}

src/kvm_ioctls.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,13 @@ ioctl_iow_nr!(KVM_SET_XCRS, KVMIO, 0xa7, kvm_xcrs);
197197
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
198198
ioctl_io_nr!(KVM_KVMCLOCK_CTRL, KVMIO, 0xad);
199199

200+
/* Available with KVM_CAP_TSC_CONTROL */
201+
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
202+
ioctl_io_nr!(KVM_SET_TSC_KHZ, KVMIO, 0xa2);
203+
/* Available with KVM_CAP_GET_TSC_KHZ */
204+
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
205+
ioctl_io_nr!(KVM_GET_TSC_KHZ, KVMIO, 0xa3);
206+
200207
/* Available with KVM_CAP_ENABLE_CAP */
201208
#[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))]
202209
ioctl_iow_nr!(KVM_ENABLE_CAP, KVMIO, 0xa3, kvm_enable_cap);

0 commit comments

Comments
 (0)