Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
e0ac0f2
block: add mirror module skeleton
Coffeeri Apr 27, 2026
5d51ed7
block: add MirroringAsyncIo skeleton
Coffeeri Apr 27, 2026
9536e55
block: add range lock primitive for mirror
Coffeeri Apr 28, 2026
9493478
block: add CompletionWaiter for single-fd waits
Coffeeri Apr 29, 2026
a470c6e
block: mirror mutating I/O to destination
Coffeeri Apr 28, 2026
6960766
block: add background copy worker for mirror
Coffeeri Apr 29, 2026
4f45301
virtio-devices: swap disk_image via queue commands
Coffeeri Apr 29, 2026
cb570bb
virtio-devices: pre-allocate per-queue command slots
Coffeeri Apr 29, 2026
7b460b6
block: add BlockMirrorHandle
Coffeeri Apr 29, 2026
8487ccf
block: add MirroringAsyncIo::create
Coffeeri Apr 30, 2026
6263c1d
virtio-devices: add Block::start_mirror
Coffeeri Apr 30, 2026
c6e5a66
virtio-devices, block: add mirror status helper
Coffeeri Apr 30, 2026
4626540
vmm: add device manager block mirror start and status
Coffeeri Apr 30, 2026
b6a01bd
vmm: add vm.disk-mirror-start REST endpoint
Coffeeri Apr 30, 2026
30046ea
vmm: add vm.disk-mirror-status REST endpoint
Coffeeri Apr 30, 2026
13619a9
block: implement MirroringAsyncIo::submit_batch_requests
Coffeeri Apr 30, 2026
60586d7
virtio-devices: drain mirror wrapper before disk_image swap
Coffeeri May 3, 2026
b39ab4e
virtio-devices, block: add Block::complete_mirror
Coffeeri May 3, 2026
6a7767c
vmm: add vm.disk-mirror-complete REST endpoint
Coffeeri May 3, 2026
f52c12b
virtio-devices, block: add Block::cancel_mirror
Coffeeri Jun 10, 2026
66c63f9
vmm: deny conflicting ops while a disk mirror runs
Coffeeri Jun 10, 2026
eb99ccd
block: use source passthrough after mirror failure
Coffeeri Jul 23, 2026
61df06c
vmm: add vm.disk-mirror-cancel REST endpoint
Coffeeri Jun 11, 2026
aa07314
block: preserve sparseness in CopyWorker
Coffeeri Jun 17, 2026
c0714c4
block: add mirror unit tests
Coffeeri Jun 22, 2026
e0585ac
block: test range guard held across mirror write
Coffeeri Jun 23, 2026
f8870aa
virtio-devices, block: reject mirror ops on a paused device
Coffeeri Jun 23, 2026
cc4bd7a
vmm: reject mirror destination already backing a disk
Coffeeri Jun 24, 2026
5e3abaf
vmm: seccomp: allow io_uring and eventfd2 on vcpus
Coffeeri Jun 24, 2026
738336e
block: test batched submit on partial failure
Coffeeri Jun 24, 2026
bb94499
block: test mirror phase transitions and op fan-out
Coffeeri Jun 24, 2026
b744756
docs: add disk mirroring guide
Coffeeri Jun 25, 2026
434161e
vmm: dedup block device lookup in DeviceManager
Coffeeri Jul 6, 2026
b2f7bdc
virtio-devices: generalize lock granularity
Coffeeri Jul 13, 2026
695aafe
virtio-devices: generalize disk locking
Coffeeri Jul 13, 2026
fb38ff3
virtio-devices, vmm: lock mirror destination
Coffeeri Jul 13, 2026
ec579b7
block: release stale QEMU lock after downgrade
Coffeeri Jul 20, 2026
dd1bf41
block: reject QCOW2 backing chains for mirroring
Coffeeri Jul 20, 2026
40cadaf
vmm: generalize postponed lifecycle events
Coffeeri Jul 21, 2026
40ce520
vmm: postpone guest lifecycle for disk mirrors
Coffeeri Jul 21, 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
9 changes: 7 additions & 2 deletions block/src/disk_file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
use std::fmt::Debug;

use crate::async_io::{AsyncIo, BorrowedDiskFd};
use crate::{BlockResult, DiskTopology};
use crate::{BlockError, BlockErrorKind, BlockResult, DiskTopology};

/// Reported capacity of a disk image.
pub trait DiskSize: Send + Debug {
Expand Down Expand Up @@ -96,7 +96,12 @@ pub trait Resizable: Send + Debug {
/// Every disk format implements `DiskSize` and `Geometry`.
/// `Sync` is required so that `Arc<dyn DiskFile>` can be shared
/// across threads for concurrent readonly access.
pub trait DiskFile: DiskSize + Geometry + Sync {}
pub trait DiskFile: DiskSize + Geometry + Sync {
/// Returns an error if this disk image cannot participate in block mirroring.
fn supports_mirroring(&self) -> BlockResult<()> {
Err(BlockError::from_kind(BlockErrorKind::UnsupportedFeature))
}
Comment thread
arctic-alpaca marked this conversation as resolved.
}

/// Full capability disk file trait.
///
Expand Down
34 changes: 34 additions & 0 deletions block/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,37 @@ use std::fmt::{self, Display, Formatter};
use std::io;
use std::path::PathBuf;

use thiserror::Error;

/// Errors reported by block mirroring operations.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Error)]
pub enum MirrorError {
/// Block mirroring does not support disk images with backing files.
#[error("Block mirroring does not support disk images with backing files")]
BackingFileUnsupported,
/// A mirror completion is already in progress.
#[error("Mirror completion already in progress")]
CompletionInProgress,
/// The mirror destination advisory lock could not be acquired.
#[error("Failed to lock the mirror destination")]
DestinationLock,
/// A mirror operation was requested before the device was activated.
#[error("Mirror operation rejected: the device is not active")]
DeviceNotActive,
/// A mirror operation was requested while the device is paused.
#[error("Mirror operation rejected: the device is paused")]
DevicePaused,
/// A mirror operation was requested but no mirror is active for the device.
#[error("No active mirror for the device")]
NotActive,
/// A completion was requested but the mirror has not reached the ready phase.
#[error("Mirror is not yet ready, cannot complete")]
NotReady,
/// A mirror swap was requested but was unsuccessful.
#[error("Failed to swap AsyncIO in virtqueue worker for mirror")]
Swap,
}

/// Small, stable classification of block errors.
///
/// Callers match on this for control flow. Adding new format specific
Expand All @@ -42,6 +73,8 @@ pub enum BlockErrorKind {
NotFound,
/// An internal counter or limit was exceeded.
Overflow,
/// A block mirroring operation failed.
Mirror(MirrorError),
}

impl Display for BlockErrorKind {
Expand All @@ -54,6 +87,7 @@ impl Display for BlockErrorKind {
Self::OutOfBounds => write!(f, "Out of bounds"),
Self::NotFound => write!(f, "Not found"),
Self::Overflow => write!(f, "Overflow"),
Self::Mirror(error) => Display::fmt(error, f),
}
}
}
Expand Down
30 changes: 30 additions & 0 deletions block/src/fcntl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,8 @@ impl LockGranularity {
let _ = self.release_unneeded_locks_qemu(file, current_lock_status);
return Err(error);
}

self.release_unneeded_locks_qemu(file, lock_type)?;
Ok(())
}

Expand Down Expand Up @@ -369,3 +371,31 @@ impl FromStr for LockGranularityChoice {
}
}
}

#[cfg(test)]
mod tests {
use std::fs::OpenOptions;

use vmm_sys_util::tempfile::TempFile;

use super::{LockGranularity, LockType};

#[test]
fn qemu_lock_downgrade_allows_another_reader() {
let disk = TempFile::new().unwrap();
let other_reader = OpenOptions::new()
.read(true)
.write(true)
.open(disk.as_path())
.unwrap();
let lock = LockGranularity::QemuCompatible;

lock.try_acquire_lock(disk.as_file(), LockType::Write, LockType::Unlock)
.unwrap();
lock.try_acquire_lock(disk.as_file(), LockType::Read, LockType::Write)
.unwrap();

lock.try_acquire_lock(&other_reader, LockType::Read, LockType::Unlock)
.unwrap();
}
}
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