Skip to content

Commit dd04187

Browse files
committed
fix: Retry KVM_CREATE_VM on EINTR
It is known that KVM_CREATE_VM fails with EINTR on heavily loaded machines with many VMs. It might be a kernel bug but apparently has not been fixed. To mitigate it, QEMU does an infinitely retry on EINTR. Similar, do retries up to 5 times. Signed-off-by: Takahiro Itazuri <[email protected]>
1 parent 8757b06 commit dd04187

File tree

2 files changed

+58
-2
lines changed

2 files changed

+58
-2
lines changed

src/vmm/src/vstate/vm/aarch64.rs

+29-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use serde::{Deserialize, Serialize};
66

77
use super::VmError;
88
use crate::arch::aarch64::gic::GicState;
9+
use crate::logger::info;
910
use crate::Kvm;
1011

1112
/// Structure representing the current architecture's understand of what a "virtual machine" is.
@@ -30,7 +31,34 @@ pub enum ArchVmError {
3031
impl ArchVm {
3132
/// Create a new `Vm` struct.
3233
pub fn new(kvm: &Kvm) -> Result<ArchVm, VmError> {
33-
let fd = kvm.fd.create_vm().map_err(VmError::CreateVm)?;
34+
// It is known that KVM_CREATE_VM spuriously fails with EINTR on heavily loaded machines
35+
// with many VMs. It might be a kernel bug but apparently has not been fixed.
36+
//
37+
// To mitigate it, QEMU does an inifinite retry on EINTR:
38+
// - https://github.com./qemu/qemu/commit/94ccff133820552a859c0fb95e33a539e0b90a75
39+
// - https://github.com./qemu/qemu/commit/bbde13cd14ad4eec18529ce0bf5876058464e124
40+
//
41+
// Similarly, we do retries up to 5 times until the bug is fixed.
42+
const MAX_ATTEMPTS: u8 = 5;
43+
let mut attempts = 0;
44+
let fd = loop {
45+
match kvm.fd.create_vm() {
46+
Ok(fd) => break fd,
47+
Err(err) if err.errno() == libc::EINTR => {
48+
attempts += 1;
49+
info!("KVM_CREATE_VM returned EINTR: {attempts} of {MAX_ATTEMPTS} attempts");
50+
if attempts == MAX_ATTEMPTS {
51+
return Err(VmError::CreateVm(err));
52+
}
53+
// Exponential backoff (1us, 2us, 4us, 8us => 15ms in total)
54+
std::thread::sleep(std::time::Duration::from_micros(
55+
2u64.pow(attempts as u32 - 1),
56+
));
57+
}
58+
Err(err) => return Err(VmError::CreateVm(err)),
59+
}
60+
};
61+
3462
Ok(ArchVm {
3563
fd,
3664
irqchip_handle: None,

src/vmm/src/vstate/vm/x86_64.rs

+29-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use kvm_ioctls::VmFd;
1111
use serde::{Deserialize, Serialize};
1212

1313
use crate::arch::x86_64::msr::MsrError;
14+
use crate::logger::info;
1415
use crate::utils::u64_to_usize;
1516
use crate::vstate::vm::VmError;
1617

@@ -53,7 +54,34 @@ pub struct ArchVm {
5354
impl ArchVm {
5455
/// Create a new `Vm` struct.
5556
pub fn new(kvm: &crate::vstate::kvm::Kvm) -> Result<ArchVm, VmError> {
56-
let fd = kvm.fd.create_vm().map_err(VmError::CreateVm)?;
57+
// It is known that KVM_CREATE_VM spuriously fails with EINTR on heavily loaded machines
58+
// with many VMs. It might be a kernel bug but apparently has not been fixed.
59+
//
60+
// To mitigate it, QEMU does an inifinite retry on EINTR:
61+
// - https://github.com./qemu/qemu/commit/94ccff133820552a859c0fb95e33a539e0b90a75
62+
// - https://github.com./qemu/qemu/commit/bbde13cd14ad4eec18529ce0bf5876058464e124
63+
//
64+
// Similarly, we do retries up to 5 times until the bug is fixed.
65+
const MAX_ATTEMPTS: u8 = 5;
66+
let mut attempts = 0;
67+
let fd = loop {
68+
match kvm.fd.create_vm() {
69+
Ok(fd) => break fd,
70+
Err(err) if err.errno() == libc::EINTR => {
71+
attempts += 1;
72+
info!("KVM_CREATE_VM returned EINTR: {attempts} of {MAX_ATTEMPTS} attempts");
73+
if attempts == MAX_ATTEMPTS {
74+
return Err(VmError::CreateVm(err));
75+
}
76+
// Exponential backoff (1us, 2us, 4us, 8us => 15ms in total)
77+
std::thread::sleep(std::time::Duration::from_micros(
78+
2u64.pow(attempts as u32 - 1),
79+
));
80+
}
81+
Err(err) => return Err(VmError::CreateVm(err)),
82+
}
83+
};
84+
5785
let msrs_to_save = kvm.msrs_to_save().map_err(ArchVmError::GetMsrsToSave)?;
5886

5987
fd.set_tss_address(u64_to_usize(crate::arch::x86_64::layout::KVM_TSS_ADDRESS))

0 commit comments

Comments
 (0)