Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
41e1ca4
block: add mirror module skeleton
Coffeeri Apr 27, 2026
d9c6045
block: add MirroringAsyncIo skeleton
Coffeeri Apr 27, 2026
3c2f3f5
block: add range lock primitive for mirror
Coffeeri Apr 28, 2026
2399787
block: add EpollWaiter for single-fd waits
Coffeeri Apr 29, 2026
27fa09a
block: mirror mutating I/O to destination
Coffeeri Apr 28, 2026
1011388
block: add background copy worker for mirror
Coffeeri Apr 29, 2026
8238fbd
virtio-devices: swap disk_image via queue commands
Coffeeri Apr 29, 2026
ba3b239
virtio-devices: pre-allocate per-queue command slots
Coffeeri Apr 29, 2026
740d7fd
block: add BlockMirrorHandle
Coffeeri Apr 29, 2026
573027f
block: add MirroringAsyncIo::create
Coffeeri Apr 30, 2026
368cfac
virtio-devices: add Block::start_mirror
Coffeeri Apr 30, 2026
2540dc6
virtio-devices, block: add mirror status helper
Coffeeri Apr 30, 2026
90b090e
vmm: add device manager block mirror start and status
Coffeeri Apr 30, 2026
59cbf6e
vmm: add vm.disk-mirror-start REST endpoint
Coffeeri Apr 30, 2026
780a24f
vmm: add vm.disk-mirror-status REST endpoint
Coffeeri Apr 30, 2026
0db8190
block: implement MirroringAsyncIo::submit_batch_requests
Coffeeri Apr 30, 2026
eed7545
block: add AsyncIo::has_inflight_requests
Coffeeri May 3, 2026
7f84688
virtio-devices: drain mirror wrapper before disk_image swap
Coffeeri May 3, 2026
0ee00f1
virtio-devices, block: add Block::complete_mirror
Coffeeri May 3, 2026
6b8d990
vmm: add vm.disk-mirror-complete REST endpoint
Coffeeri May 3, 2026
6af76b5
virtio-devices, block: add Block::cancel_mirror
Coffeeri Jun 10, 2026
b37eb5d
vmm: deny conflicting ops while a disk mirror runs
Coffeeri Jun 10, 2026
d9ce5a1
vmm: add vm.disk-mirror-cancel REST endpoint
Coffeeri Jun 11, 2026
ea69cad
block: preserve sparseness in CopyWorker
Coffeeri Jun 17, 2026
7011d86
block: add mirror unit tests
Coffeeri Jun 22, 2026
7247a84
block: test range guard held across mirror write
Coffeeri Jun 23, 2026
306c1b6
virtio-devices, block: reject mirror ops on a paused device
Coffeeri Jun 23, 2026
f22f06f
vmm: reject mirror destination already backing a disk
Coffeeri Jun 24, 2026
e7dd6de
vmm: seccomp: allow io_uring and eventfd2 on vcpus
Coffeeri Jun 24, 2026
81a8efc
block: test batched submit on partial failure
Coffeeri Jun 24, 2026
4b89e5e
block: test mirror phase transitions and op fan-out
Coffeeri Jun 24, 2026
030f349
docs: add disk mirroring guide
Coffeeri Jun 25, 2026
403fb4e
vmm: add configurable mirror destination mode
Coffeeri Jun 29, 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