Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
fac592f
block: add mirror module skeleton
Coffeeri Apr 27, 2026
330427b
block: add MirroringAsyncIo skeleton
Coffeeri Apr 27, 2026
d645c02
block: add range lock primitive for mirror
Coffeeri Apr 28, 2026
6863763
block: add EpollWaiter for single-fd waits
Coffeeri Apr 29, 2026
e4ff3d9
block: mirror mutating I/O to destination
Coffeeri Apr 28, 2026
b384a82
block: add background copy worker for mirror
Coffeeri Apr 29, 2026
e979117
virtio-devices: swap disk_image via queue commands
Coffeeri Apr 29, 2026
f21c56e
virtio-devices: pre-allocate per-queue command slots
Coffeeri Apr 29, 2026
b1097ef
block: add BlockMirrorHandle
Coffeeri Apr 29, 2026
300d498
block: add MirroringAsyncIo::create
Coffeeri Apr 30, 2026
22b2701
virtio-devices: add Block::start_mirror
Coffeeri Apr 30, 2026
6a2e19d
virtio-devices, block: add mirror status helper
Coffeeri Apr 30, 2026
4502534
vmm: add device manager block mirror start and status
Coffeeri Apr 30, 2026
f80ceac
vmm: add vm.disk-mirror-start REST endpoint
Coffeeri Apr 30, 2026
4ead677
vmm: add vm.disk-mirror-status REST endpoint
Coffeeri Apr 30, 2026
577b85d
block: implement MirroringAsyncIo::submit_batch_requests
Coffeeri Apr 30, 2026
92cefc1
block: add AsyncIo::has_inflight_requests
Coffeeri May 3, 2026
b3f8436
virtio-devices: drain mirror wrapper before disk_image swap
Coffeeri May 3, 2026
3a1dbd8
virtio-devices, block: add Block::complete_mirror
Coffeeri May 3, 2026
dc0f4a6
vmm: add vm.disk-mirror-complete REST endpoint
Coffeeri May 3, 2026
c41c63d
virtio-devices, block: add Block::cancel_mirror
Coffeeri Jun 10, 2026
3ad1090
vmm: deny conflicting ops while a disk mirror runs
Coffeeri Jun 10, 2026
64af609
vmm: add vm.disk-mirror-cancel REST endpoint
Coffeeri Jun 11, 2026
b71be3d
block: preserve sparseness in CopyWorker
Coffeeri Jun 17, 2026
21adedf
block: add mirror unit tests
Coffeeri Jun 22, 2026
24bd855
block: test range guard held across mirror write
Coffeeri Jun 23, 2026
4ae80bb
virtio-devices, block: reject mirror ops on a paused device
Coffeeri Jun 23, 2026
c0f5652
vmm: reject mirror destination already backing a disk
Coffeeri Jun 24, 2026
3914a97
vmm: seccomp: allow io_uring and eventfd2 on vcpus
Coffeeri Jun 24, 2026
2e159dc
block: test batched submit on partial failure
Coffeeri Jun 24, 2026
ecfa9cd
block: test mirror phase transitions and op fan-out
Coffeeri Jun 24, 2026
9a65511
docs: add disk mirroring guide
Coffeeri Jun 25, 2026
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 block/src/async_io.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,4 +109,12 @@ pub trait AsyncIo: Send {
fn alignment(&self) -> u64 {
SECTOR_SIZE
}

/// Returns true when this implementation has request pairings in flight
/// that have not yet been acked to the guest. Only the mirroring
/// implementation tracks such pairings, plain backends always return
/// false.
fn has_inflight_requests(&self) -> bool {
false
}
}
20 changes: 20 additions & 0 deletions block/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,18 @@ pub enum BlockErrorKind {
NotFound,
/// An internal counter or limit was exceeded.
Overflow,
/// The file already exists, when disk creation was requested.
AlreadyExists,
/// A mirror operation was requested but no mirror is active for the device.
MirrorNotActive,
/// A completion was requested but the mirror has not reached the ready phase.
MirrorNotReady,
/// A mirror swap was requested but was unsuccessful.
MirrorSwap,
/// A mirror completion is already in progress.
MirrorCompletionInProgress,
/// A mirror operation was requested while the device is paused.
MirrorDevicePaused,
}

impl Display for BlockErrorKind {
Expand All @@ -54,6 +66,14 @@ impl Display for BlockErrorKind {
Self::OutOfBounds => write!(f, "Out of bounds"),
Self::NotFound => write!(f, "Not found"),
Self::Overflow => write!(f, "Overflow"),
Self::AlreadyExists => write!(f, "Already exists"),
Self::MirrorNotActive => write!(f, "No active mirror for the device"),
Self::MirrorNotReady => write!(f, "Mirror is not yet ready, cannot complete"),
Self::MirrorSwap => write!(f, "Failed to swap AsyncIO in virtqueue worker for mirror"),
Self::MirrorCompletionInProgress => write!(f, "Mirror completion already in progress"),
Self::MirrorDevicePaused => {
write!(f, "Mirror operation rejected: the device is paused")
}
}
}
}
Expand Down
42 changes: 42 additions & 0 deletions block/src/factory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ use crate::block_io_uring_is_supported;
use crate::disk_file::AsyncFullDiskFile;
use crate::error::{BlockError, BlockErrorKind, BlockResult};
use crate::fixed_vhd_disk::FixedVhdDisk;
use crate::qcow::{QcowFile, RawFile};
use crate::qcow_disk::QcowDisk;
use crate::raw_disk::{RawBackend, RawDisk};
use crate::vhdx_sync::VhdxDiskSync;
Expand Down Expand Up @@ -203,6 +204,47 @@ fn open_qcow2(
))
}

/// Create a new disk image at `options.path` of the given image type
/// and logical `size`. The file must not exist yet.
pub fn create_disk(
options: &DiskOpenOptions<'_>,
image_type: ImageType,
size: u64,
) -> BlockResult<()> {
if options.path.exists() {
return Err(BlockError::from_kind(BlockErrorKind::AlreadyExists).with_path(options.path));
}
let file = fs::OpenOptions::new()
.read(true)
.write(true)
.create_new(true)
.open(options.path)
.map_err(|e| {
BlockError::from_kind(BlockErrorKind::Io)
.with_path(options.path)
.with_source(e)
})?;

match image_type {
ImageType::Raw => {
file.set_len(size)
.map_err(|e| BlockError::from(e).with_path(options.path))?;
}
ImageType::Qcow2 => {
let raw_file = RawFile::new(file.try_clone()?, options.direct);
QcowFile::new(raw_file, 3, size, options.sparse)
.map_err(|e| e.with_path(options.path))?;
}
_ => {
return Err(
BlockError::from_kind(BlockErrorKind::UnsupportedFeature).with_path(options.path)
);
}
}

Ok(())
}

#[cfg(test)]
mod unit_tests {
use std::io::Write;
Expand Down
1 change: 1 addition & 0 deletions block/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ pub mod fixed_vhd;
pub mod fixed_vhd_async;
pub mod fixed_vhd_disk;
pub mod fixed_vhd_sync;
pub mod mirror;
pub mod qcow;
#[cfg(feature = "io_uring")]
pub(crate) mod qcow_async;
Expand Down
Loading
Loading