Skip to content

Commit 627be13

Browse files
committed
Support RTC updates from illumos 15462
This also addresses some backward-compatibility issues around the vmm-data interface, for both the VMM_ARCH and VMM_TIME classes.
1 parent 0793263 commit 627be13

File tree

13 files changed

+258
-137
lines changed

13 files changed

+258
-137
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

bin/propolis-server/src/lib/initializer.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use std::fs::File;
33
use std::io::{Error, ErrorKind};
44
use std::num::NonZeroUsize;
55
use std::sync::Arc;
6-
use std::time::SystemTime;
6+
use std::time::{SystemTime, UNIX_EPOCH};
77

88
use oximeter::types::ProducerRegistry;
99
use propolis::block;
@@ -146,7 +146,11 @@ impl<'a> MachineInitializer<'a> {
146146

147147
let rtc = &self.machine.kernel_devs.rtc;
148148
rtc.memsize_to_nvram(lowmem as u32, highmem as u64)?;
149-
rtc.set_time(SystemTime::now())?;
149+
rtc.set_time(
150+
SystemTime::now()
151+
.duration_since(UNIX_EPOCH)
152+
.expect("system time precedes UNIX epoch"),
153+
)?;
150154

151155
Ok(())
152156
}

bin/propolis-standalone/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ path = "src/main.rs"
1010

1111
[dependencies]
1212
anyhow.workspace = true
13+
bhyve_api.workspace = true
1314
clap = { workspace = true, features = ["derive"] }
1415
ctrlc.workspace = true
1516
futures.workspace = true

bin/propolis-standalone/src/main.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use std::io::{Error, ErrorKind, Result};
44
use std::path::Path;
55
use std::sync::atomic::{AtomicUsize, Ordering};
66
use std::sync::{Arc, Condvar, Mutex, MutexGuard};
7-
use std::time::SystemTime;
7+
use std::time::{SystemTime, UNIX_EPOCH};
88

99
use anyhow::Context;
1010
use clap::Parser;
@@ -709,7 +709,11 @@ fn setup_instance(
709709

710710
let rtc = &machine.kernel_devs.rtc;
711711
rtc.memsize_to_nvram(lowmem as u32, highmem as u64)?;
712-
rtc.set_time(SystemTime::now())?;
712+
rtc.set_time(
713+
SystemTime::now()
714+
.duration_since(UNIX_EPOCH)
715+
.expect("system time precedes UNIX epoch"),
716+
)?;
713717

714718
let (power_pin, reset_pin) = inst.generate_pins();
715719
let pci_topo =

bin/propolis-standalone/src/snapshot.rs

Lines changed: 60 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,19 @@ use std::path::Path;
1818
use std::sync::Arc;
1919

2020
use anyhow::Context;
21+
use bhyve_api::{
22+
vdi_field_entry_v1, vdi_time_info_v1, ApiVersion, VAI_BOOT_HRTIME,
23+
VDC_VMM_ARCH, VDC_VMM_TIME,
24+
};
25+
use propolis::vmm::data as vmm_data;
2126
use propolis::{
2227
chardev::UDSock,
2328
common::{GuestAddr, GuestRegion},
2429
inventory::Order,
2530
migrate::{MigrateCtx, Migrator},
31+
vmm::VmmHdl,
2632
};
33+
2734
use slog::{info, warn};
2835
use tokio::{
2936
fs::File,
@@ -57,9 +64,9 @@ pub(crate) async fn save(
5764

5865
info!(log, "Serializing global VM state");
5966
let global_state = {
60-
let global_state =
61-
hdl.export().context("Failed to export global VM state")?;
62-
serde_json::to_vec(&global_state)?
67+
let output =
68+
export_global(&hdl).context("Failed to export global VM state")?;
69+
serde_json::to_vec(&output)?
6370
};
6471

6572
info!(log, "Serializing VM device state");
@@ -223,13 +230,12 @@ pub(crate) async fn restore(
223230
let state_len = file.read_u64().await?;
224231
let mut global_state = vec![0; state_len.try_into()?];
225232
file.read_exact(&mut global_state).await?;
226-
let mut deserializer =
227-
serde_json::Deserializer::from_slice(&global_state);
228-
let deserializer =
229-
&mut <dyn erased_serde::Deserializer>::erase(&mut deserializer);
230233

231-
// Restore it
232-
hdl.import(deserializer).context("Failed to import global VM state")?;
234+
let data: VmGlobalState = serde_json::from_slice(&global_state)
235+
.context("Failed to deserialize global VM state")?;
236+
237+
import_global(&hdl, &data)
238+
.context("failed to import global VM state")?;
233239
}
234240

235241
// Next are the devices
@@ -333,3 +339,48 @@ pub(crate) async fn restore(
333339
drop(inst_inner);
334340
Ok((inst, com1_sock))
335341
}
342+
343+
#[derive(serde::Serialize, serde::Deserialize)]
344+
pub struct VmGlobalState {
345+
// Just using the raw boot_hrtime leaves room for all sorts of failures,
346+
// especially if a saved state file is used after a subsequent reboot of the
347+
// host. These problems can be addressed later.
348+
pub boot_hrtime: i64,
349+
// Fixing up the guest TSC is left as an exercise for later
350+
}
351+
352+
fn export_global(hdl: &VmmHdl) -> std::io::Result<VmGlobalState> {
353+
if hdl.api_version()? > ApiVersion::V11.into() {
354+
let info: vdi_time_info_v1 = vmm_data::read(hdl, -1, VDC_VMM_TIME, 1)?;
355+
356+
Ok(VmGlobalState { boot_hrtime: info.vt_boot_hrtime })
357+
} else {
358+
let arch_entries: Vec<bhyve_api::vdi_field_entry_v1> =
359+
vmm_data::read_many(hdl, -1, VDC_VMM_ARCH, 1)?;
360+
let boot_ent = arch_entries
361+
.iter()
362+
.find(|ent| ent.vfe_ident == VAI_BOOT_HRTIME)
363+
.expect("VAI_BOOT_HRTIME should be present");
364+
365+
Ok(VmGlobalState { boot_hrtime: boot_ent.vfe_value as i64 })
366+
}
367+
}
368+
fn import_global(hdl: &VmmHdl, state: &VmGlobalState) -> std::io::Result<()> {
369+
if hdl.api_version()? > ApiVersion::V11.into() {
370+
let mut info: vdi_time_info_v1 =
371+
vmm_data::read(hdl, -1, VDC_VMM_TIME, 1)?;
372+
373+
info.vt_boot_hrtime = state.boot_hrtime;
374+
vmm_data::write(hdl, -1, VDC_VMM_TIME, 1, info)?;
375+
376+
Ok(())
377+
} else {
378+
let arch_entry = vdi_field_entry_v1 {
379+
vfe_ident: VAI_BOOT_HRTIME,
380+
vfe_value: state.boot_hrtime as u64,
381+
..Default::default()
382+
};
383+
vmm_data::write(hdl, -1, VDC_VMM_ARCH, 1, arch_entry)?;
384+
Ok(())
385+
}
386+
}

bin/propolis-utils/src/bin/savex.rs

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -44,16 +44,10 @@ fn print_globals(mut fp: File, len: u64) -> anyhow::Result<()> {
4444
let mut buf = vec![0u8; len as usize];
4545
fp.read_exact(&mut buf[..])?;
4646

47-
let data: propolis::vmm::migrate::BhyveVmV1 = serde_json::from_slice(&buf)?;
47+
let data: serde_json::Value = serde_json::from_slice(&buf)?;
4848

49-
println!("Global entries:");
50-
if !data.arch_entries.is_empty() {
51-
for ent in data.arch_entries {
52-
println!("{:08X} = {}", ent.ident, ent.value);
53-
}
54-
} else {
55-
println!("None");
56-
}
49+
println!("Global data:");
50+
println!("{:?}", data);
5751

5852
Ok(())
5953
}

crates/bhyve-api/header-check/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ libc = "0.2"
1212
[build-dependencies]
1313
cc = "1"
1414
ctest2 = "0.4"
15+
# Build-time conditions depend on the max API version defined in the crate
16+
bhyve_api_sys = { path = "../sys" }
1517

1618
[[test]]
1719
name = "main"

crates/bhyve-api/header-check/build.rs

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,31 @@
33
use std::convert::TryFrom;
44
use std::env;
55
use std::path::PathBuf;
6+
use std::str::FromStr;
7+
use std::sync::atomic::{AtomicU32, Ordering};
8+
9+
extern crate bhyve_api_sys;
10+
use bhyve_api_sys::VMM_CURRENT_INTERFACE_VERSION;
11+
12+
static CHECK_VERSION: AtomicU32 = AtomicU32::new(VMM_CURRENT_INTERFACE_VERSION);
13+
14+
/// Source checked against has API version greater than `ver` argument
15+
fn ver_gt(ver: u32) -> bool {
16+
CHECK_VERSION.load(Ordering::Relaxed) > ver
17+
}
18+
/// Source checked against has API version less than `ver` argument
19+
fn ver_lt(ver: u32) -> bool {
20+
CHECK_VERSION.load(Ordering::Relaxed) < ver
21+
}
22+
/// Source checked against has API version equal to `ver` argument
23+
fn ver_eq(ver: u32) -> bool {
24+
CHECK_VERSION.load(Ordering::Relaxed) == ver
25+
}
626

727
fn main() {
828
let mut cfg = ctest2::TestGenerator::new();
929

30+
// We cannot proceed without a path to the source
1031
let gate_dir = match env::var("GATE_SRC").map(PathBuf::try_from) {
1132
Ok(Ok(dir)) => dir,
1233
_ => {
@@ -15,6 +36,26 @@ fn main() {
1536
}
1637
};
1738

39+
// Allow the user to specify a target interface version to check against.
40+
match env::var("API_VERSION").ok().map(|v| u32::from_str(&v)) {
41+
Some(Ok(ver)) => {
42+
if ver > VMM_CURRENT_INTERFACE_VERSION {
43+
eprintln!(
44+
"API_VERSION {} cannot be > \
45+
VMM_CURRENT_INTERFACE_VERSION ({})",
46+
ver, VMM_CURRENT_INTERFACE_VERSION
47+
);
48+
std::process::exit(1);
49+
}
50+
CHECK_VERSION.store(ver, Ordering::Relaxed);
51+
}
52+
Some(Err(e)) => {
53+
eprintln!("Invalid API_VERSION {:?}", e);
54+
std::process::exit(1);
55+
}
56+
_ => {}
57+
}
58+
1859
let include_paths = [
1960
// For #include_next to work, these need to be first
2061
"usr/src/compat/bhyve",
@@ -46,6 +87,23 @@ fn main() {
4687
// We expose our own copy for now for us as a constraint.
4788
"VM_MAXCPU" => true,
4889

90+
// Do not bother checking the version definition define if we are
91+
// assuming the source is from a different version.
92+
"VMM_CURRENT_INTERFACE_VERSION"
93+
if !ver_eq(VMM_CURRENT_INTERFACE_VERSION) =>
94+
{
95+
true
96+
}
97+
98+
// API V11 saw the removal of several time-realted VMM_ARCH defines
99+
"VAI_TSC_BOOT_OFFSET" | "VAI_BOOT_HRTIME" | "VAI_TSC_FREQ"
100+
if ver_gt(10) =>
101+
{
102+
true
103+
}
104+
// API V11 saw the addition of the VMM_TIME data class
105+
"VDC_VMM_TIME" if ver_lt(11) => true,
106+
49107
_ => false,
50108
});
51109

@@ -64,6 +122,14 @@ fn main() {
64122
"vm_inst_emul" => true,
65123
"vm_paging" => true,
66124

125+
// In API V12, the RTC data struct was revised to v2, with the old v1
126+
// definition being removed.
127+
"vdi_rtc_v1" if ver_gt(11) => true,
128+
"vdi_rtc_v2" if ver_lt(12) => true,
129+
130+
// VMM_TIME struct added in API V11
131+
"vdi_time_info_v1" if ver_lt(11) => true,
132+
67133
_ => false,
68134
});
69135

0 commit comments

Comments
 (0)