Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,14 @@ as a virtual machine's data disk(s) (`/dev/vd[b-z]`).

- `path`: Path to the disk image file.
- `format`: Format of the disk image. Supported formats: raw, qcow2.
- `sync`: (Optional) How guest flush requests are carried out on the host. Unset, libkrun's
platform default applies, which on macOS is `relaxed`.
- `full`: on macOS, `fcntl(F_FULLFSYNC)`, which asks the drive to flush all buffered data to
permanent storage.
- `relaxed`: on macOS, `fsync(2)`, which moves data to the drive but does not ask the drive to
flush its own buffers; per fsync(2), on a drive power loss some or none of that data may be
written, possibly out of order. Same as `full` on Linux.
- `none`: the device does not offer flush to the guest.

#### Example

Expand Down
27 changes: 26 additions & 1 deletion src/cmdline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,29 @@ mod tests {
assert_eq!(in_order, expected);
}

#[test]
fn virtio_blk_sync_mode() {
use crate::virtio::{BlkConfig, DiskSyncMode};
use std::str::FromStr;

let blk = |s: &str| BlkConfig::from_str(s).map(|b| b.sync);

assert_eq!(blk("path=/Users/user/d.raw").unwrap(), None);
assert_eq!(
blk("path=/Users/user/d.raw,sync=none").unwrap(),
Some(DiskSyncMode::None)
);
assert_eq!(
blk("path=/Users/user/d.raw,sync=relaxed").unwrap(),
Some(DiskSyncMode::Relaxed)
);
assert_eq!(
blk("path=/Users/user/d.raw,sync=full").unwrap(),
Some(DiskSyncMode::Full)
);
assert!(blk("path=/Users/user/d.raw,sync=fsync").is_err());
}

#[test]
fn virtio_net_argument_ordering() {
let in_order = super::parse_args(String::from(
Expand Down Expand Up @@ -480,7 +503,7 @@ mod tests {
"--device",
"virtio-serial,logFilePath=/Users/user/serial.log",
"--device",
"virtio-blk,path=/Users/user/data.raw,format=raw",
"virtio-blk,path=/Users/user/data.raw,format=raw,sync=full",
"--device",
"virtio-vsock,port=1024,socketURL=/Users/user/vsock1.sock,listen",
"--device",
Expand Down Expand Up @@ -703,6 +726,7 @@ mod tests {
if let VirtioDeviceConfig::Blk(blk) = blk {
assert_eq!(blk.path, PathBuf::from_str("/Users/user/data.raw").unwrap());
assert_eq!(blk.format, DiskImageFormat::Raw);
assert_eq!(blk.sync, Some(DiskSyncMode::Full));
} else {
panic!("expected virtio-blk device as 4th device config argument");
}
Expand Down Expand Up @@ -739,6 +763,7 @@ mod tests {
PathBuf::from_str("/Users/user/root.qcow2").unwrap()
);
assert_eq!(blk.format, DiskImageFormat::Qcow2);
assert_eq!(blk.sync, None);
} else {
panic!("expected virtio-blk device as 1st device config argument");
}
Expand Down
68 changes: 60 additions & 8 deletions src/virtio.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,15 @@ extern "C" {
disk_format: u32,
read_only: bool,
) -> i32;
fn krun_add_disk3(
ctx_id: u32,
c_block_id: *const c_char,
c_disk_path: *const c_char,
disk_format: u32,
read_only: bool,
direct_io: bool,
sync_mode: u32,
) -> i32;
fn krun_add_vsock_port2(ctx_id: u32, port: u32, c_filepath: *const c_char, listen: bool)
-> i32;
fn krun_add_virtiofs4(
Expand Down Expand Up @@ -99,6 +108,31 @@ impl FromStr for DiskImageFormat {
}
}

/// How a virtio-blk device honors guest flush requests. Values match KRUN_SYNC_* in libkrun.h.
#[repr(u32)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum DiskSyncMode {
/// Ignore VIRTIO_BLK_F_FLUSH.
None = 0,
/// Honor flushes; on macOS, fsync(2) without asking the drive to flush its cache.
Relaxed = 1,
/// Honor flushes down to physical media (F_FULLFSYNC on macOS).
Full = 2,
}

impl FromStr for DiskSyncMode {
type Err = anyhow::Error;

fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_lowercase().as_str() {
"none" => Ok(DiskSyncMode::None),
"relaxed" => Ok(DiskSyncMode::Relaxed),
"full" => Ok(DiskSyncMode::Full),
_ => Err(anyhow!("unsupported disk sync mode")),
}
}
}

#[derive(Clone, Debug, Default, PartialEq)]
pub enum FsPermissions {
Complete = 0,
Expand Down Expand Up @@ -194,6 +228,9 @@ pub struct BlkConfig {

/// Format of the disk image.
pub format: DiskImageFormat,

/// Flush handling. None leaves the choice to libkrun's platform default.
pub sync: Option<DiskSyncMode>,
}

impl FromStr for BlkConfig {
Expand All @@ -212,6 +249,10 @@ impl FromStr for BlkConfig {
blk_config.format = DiskImageFormat::from_str(f.as_str())?;
}

if let Some(s) = args.remove("sync") {
blk_config.sync = Some(DiskSyncMode::from_str(s.as_str())?);
}

check_unknown_args(args, "virtio-blk")?;

Ok(blk_config)
Expand All @@ -228,14 +269,25 @@ impl KrunContextSet for BlkConfig {
let block_id_cstr = CString::new(basename).context("can't convert basename to cstring")?;
let path_cstr = path_to_cstring(&self.path)?;

if krun_add_disk2(
id,
block_id_cstr.as_ptr(),
path_cstr.as_ptr(),
self.format as u32,
false,
) < 0
{
let ret = match self.sync {
None => krun_add_disk2(
id,
block_id_cstr.as_ptr(),
path_cstr.as_ptr(),
self.format as u32,
false,
),
Some(sync) => krun_add_disk3(
id,
block_id_cstr.as_ptr(),
path_cstr.as_ptr(),
self.format as u32,
false,
false,
sync as u32,
),
};
if ret < 0 {
return Err(anyhow!(format!(
"unable to set virtio-blk disk for {}",
self.path.display()
Expand Down
Loading