Skip to content

Commit 9f7cfba

Browse files
authored
Hoist the XCOMP_BV read into MSHV vCPU creation (#1912)
Currently, every MSHV vCPU reset calls `get_xsave` to copy XCOMP_BV into the zeroed XSAVE area. This PR hoists that read into vCPU creation, and every reset reuses the value. This saves one hypercall per `Sandbox::restore()`. This is safe because XCOMP_BV depends only on the partition's XSAVE features, which are fixed at partition initialization and unaffected by guest state or snapshots. ## Benchmarks `mshv3`: AMD EPYC 7763, Linux 6.6 MSHV. | Benchmark | Before | After | Change | |---|---|---|---| | `snapshots/restore/default` | 87.9 µs | 66.0 µs | −25% | | `snapshots/restore/small` | 88.6 µs | 66.5 µs | −25% | | `snapshots/restore/medium` | 99.4 µs | 77.9 µs | −22% | | `guest_calls/call_with_restore/default` | 156.8 µs | 133.9 µs | −15% | | `guest_calls/call_with_restore/medium` | 165.5 µs | 140.6 µs | −15% | | `sandboxes/create_initialized/default` | 1.48 ms | 1.53 ms | +3% | | `sandboxes/sandbox_from_snapshot/default` | 598 µs | 624 µs | +4% | Each restore saves one `get_xsave` hypercall, about 22 µs. Each vCPU creation makes that hypercall once. Signed-off-by: Ludvig Liljenberg <4257730+ludfjig@users.noreply.github.com>
1 parent 558732b commit 9f7cfba

3 files changed

Lines changed: 15 additions & 16 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
6161
architecture-independent exception handling hooks. The new
6262
interfaces are significantly more liminited, but more portable; they
6363
will be extended in the future.
64+
* MSHV: vCPU creation reads the partition's XSAVE format once. This removes
65+
one hypercall from every restore.
6466

6567
### Removed
6668
* `RunPool` and the run-specific `AllocError::InvalidAlign` variant.

‎src/hyperlight_host/src/hypervisor/virtual_machine/mod.rs‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,9 +110,9 @@ pub(crate) enum HypervisorType {
110110
}
111111

112112
/// Minimum XSAVE buffer size: 512 bytes legacy region + 64 bytes header.
113-
/// Only used by MSHV and WHP which use compacted XSAVE format and need to
113+
/// Only used by WHP which uses compacted XSAVE format and needs to
114114
/// validate buffer size before accessing XCOMP_BV.
115-
#[cfg(all(target_arch = "x86_64", any(mshv3, target_os = "windows")))]
115+
#[cfg(all(target_arch = "x86_64", target_os = "windows"))]
116116
pub(crate) const XSAVE_MIN_SIZE: usize = 576;
117117

118118
/// Standard XSAVE buffer size (4KB) used by KVM and MSHV.

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

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ use crate::hypervisor::virtual_machine::XSAVE_BUFFER_SIZE;
4848
use crate::hypervisor::virtual_machine::x86_64::hw_interrupts::TimerThread;
4949
use crate::hypervisor::virtual_machine::{
5050
CreateVmError, MapMemoryError, RegisterError, RunVcpuError, UnmapMemoryError, VirtualMachine,
51-
VmExit, XSAVE_MIN_SIZE,
51+
VmExit,
5252
};
5353
use crate::mem::memory_region::{MemoryRegion, MemoryRegionFlags};
5454
#[cfg(feature = "trace_guest")]
@@ -155,6 +155,8 @@ pub(crate) struct MshvVm {
155155
#[cfg(not(feature = "hw-interrupts"))]
156156
vm_fd: VmFd,
157157
vcpu_fd: VcpuFd,
158+
/// XCOMP_BV from the XSAVE header. Fixed for the life of the partition.
159+
xcomp_bv: [u8; 8],
158160
/// Handle to the background timer (if started).
159161
#[cfg(feature = "hw-interrupts")]
160162
timer: Option<TimerThread>,
@@ -209,12 +211,19 @@ impl MshvVm {
209211
#[cfg(feature = "hw-interrupts")]
210212
Self::init_lapic(&vcpu_fd)?;
211213

214+
let xsave = vcpu_fd
215+
.get_xsave()
216+
.map_err(|e| CreateVmError::InitializeVm(e.into()))?;
217+
let mut xcomp_bv = [0u8; 8];
218+
xcomp_bv.copy_from_slice(&xsave.buffer[520..528]);
219+
212220
Ok(Self {
213221
#[cfg(feature = "hw-interrupts")]
214222
vm_fd: Arc::new(vm_fd),
215223
#[cfg(not(feature = "hw-interrupts"))]
216224
vm_fd,
217225
vcpu_fd,
226+
xcomp_bv,
218227
#[cfg(feature = "hw-interrupts")]
219228
timer: None,
220229
})
@@ -569,22 +578,10 @@ impl VirtualMachine for MshvVm {
569578
}
570579

571580
fn reset_xsave(&self) -> std::result::Result<(), RegisterError> {
572-
let current_xsave = self
573-
.vcpu_fd
574-
.get_xsave()
575-
.map_err(|e| RegisterError::GetXsave(e.into()))?;
576-
if current_xsave.buffer.len() < XSAVE_MIN_SIZE {
577-
// Minimum: 512 legacy + 64 header
578-
return Err(RegisterError::XsaveSizeMismatch {
579-
expected: XSAVE_MIN_SIZE as u32,
580-
actual: current_xsave.buffer.len() as u32,
581-
});
582-
}
583-
584581
let mut buf = XSave::default(); // default is zeroed 4KB buffer
585582

586583
// Copy XCOMP_BV (offset 520-527) - preserves feature mask + compacted bit
587-
buf.buffer[520..528].copy_from_slice(&current_xsave.buffer[520..528]);
584+
buf.buffer[520..528].copy_from_slice(&self.xcomp_bv);
588585

589586
// XSAVE area layout from Intel SDM Vol. 1 Section 13.4.1:
590587
// - Bytes 0-1: FCW (x87 FPU Control Word)

0 commit comments

Comments
 (0)