Skip to content

Commit 67561dc

Browse files
committed
Disable KVM_X86_QUIRK_SLOT_ZAP_ALL
By default, deleting a memslot flushes the guest mappings of every memslot. KVM keeps that flush as a workaround for VMs with assigned GPUs. Hyperlight assigns no devices and disables the quirk when the kernel supports it (Linux 6.12 and later). Deleting a memslot then flushes only that memslot's mappings. Snapshot capture deletes a memslot when a layer drops out of the snapshot. Restore to another snapshot and region unmapping also delete memslots. Criterion on KVM (Linux 6.17): * snapshots/call_and_create: 24% to 58% faster * snapshots/restore_alternate: 32% to 52% faster Signed-off-by: Ludvig Liljenberg <4257730+ludfjig@users.noreply.github.com>
1 parent d435d35 commit 67561dc

2 files changed

Lines changed: 28 additions & 2 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
7777
* Related snapshots share immutable memory layers. This reduces capture time
7878
and memory use. A guest can read data it overwrote before a snapshot from the
7979
older layers of that snapshot.
80+
* KVM: deleting a memory slot flushes only that slot's guest mappings on Linux
81+
6.12 and later. This speeds up the first guest call after a snapshot,
82+
restore, or unmap changes memory slots.
8083
* **Breaking:** `Sandbox::snapshot` installs the captured snapshot into the
8184
sandbox and removes `map_region` and `map_file_cow` regions. Pages of these
8285
regions that the guest has not mapped into its page tables are lost.

‎src/hyperlight_host/src/hypervisor/virtual_machine/kvm/x86_64.rs‎

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ use hyperlight_common::outb::VmAction;
77
#[cfg(gdb)]
88
use kvm_bindings::kvm_guest_debug;
99
use kvm_bindings::{
10-
Msrs, kvm_debugregs, kvm_fpu, kvm_msr_entry, kvm_regs, kvm_sregs, kvm_userspace_memory_region,
11-
kvm_xsave,
10+
KVM_CAP_DISABLE_QUIRKS2, KVM_X86_QUIRK_SLOT_ZAP_ALL, Msrs, kvm_debugregs, kvm_enable_cap,
11+
kvm_fpu, kvm_msr_entry, kvm_regs, kvm_sregs, kvm_userspace_memory_region, kvm_xsave,
1212
};
1313
use kvm_ioctls::Cap::UserMemory;
1414
use kvm_ioctls::{
@@ -175,6 +175,29 @@ impl KvmVm {
175175
.create_vm_with_type(0)
176176
.map_err(|e| CreateVmError::CreateVmFd(e.into()))?;
177177

178+
// Deleting a memslot flushes the guest mappings of every memslot unless
179+
// this quirk is disabled. KVM keeps the flush as a workaround for
180+
// assigned GPUs. Hyperlight assigns no devices.
181+
let quirks = vm_fd.check_extension_raw(KVM_CAP_DISABLE_QUIRKS2.into());
182+
// `quirks` has one bit per quirk this kernel can disable.
183+
if quirks <= 0 || quirks & KVM_X86_QUIRK_SLOT_ZAP_ALL as i32 == 0 {
184+
tracing::debug!(
185+
"Host kernel cannot disable KVM_X86_QUIRK_SLOT_ZAP_ALL. Each memory slot \
186+
deletion invalidates guest mappings in all slots, which adds latency to the \
187+
first guest call after a snapshot, restore, or unmap. Host kernel version \
188+
6.12 or later enables per-slot invalidation."
189+
);
190+
} else {
191+
let cap = kvm_enable_cap {
192+
cap: KVM_CAP_DISABLE_QUIRKS2,
193+
args: [KVM_X86_QUIRK_SLOT_ZAP_ALL.into(), 0, 0, 0],
194+
..Default::default()
195+
};
196+
vm_fd
197+
.enable_cap(&cap)
198+
.map_err(|e| CreateVmError::InitializeVm(e.into()))?;
199+
}
200+
178201
#[cfg(feature = "hw-interrupts")]
179202
let timer_irq_eventfd = Self::setup_irqfd(&vm_fd)?;
180203

0 commit comments

Comments
 (0)