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
ac77f75
virtio-devices: swap disk_image via queue commands
Coffeeri Apr 29, 2026
4ed18dc
virtio-devices: pre-allocate per-queue command slots
Coffeeri Apr 29, 2026
824922e
block: add BlockMirrorHandle
Coffeeri Apr 29, 2026
1581634
block: add MirroringAsyncIo::create
Coffeeri Apr 30, 2026
b664c14
virtio-devices: add Block::start_mirror
Coffeeri Apr 30, 2026
caf647c
virtio-devices, block: add mirror status helper
Coffeeri Apr 30, 2026
9e68673
vmm: add device manager block mirror start and status
Coffeeri Apr 30, 2026
417abab
vmm: add vm.disk-mirror-start REST endpoint
Coffeeri Apr 30, 2026
043f6a3
vmm: add vm.disk-mirror-status REST endpoint
Coffeeri Apr 30, 2026
6de6058
block: implement MirroringAsyncIo::submit_batch_requests
Coffeeri Apr 30, 2026
1dc4d39
virtio-devices: drain mirror wrapper before disk_image swap
Coffeeri May 3, 2026
1f43114
virtio-devices, block: add Block::complete_mirror
Coffeeri May 3, 2026
c66101b
vmm: add vm.disk-mirror-complete REST endpoint
Coffeeri May 3, 2026
e5d19f9
virtio-devices, block: add Block::cancel_mirror
Coffeeri Jun 10, 2026
a8879d5
vmm: deny conflicting ops while a disk mirror runs
Coffeeri Jun 10, 2026
ee56dec
block: use source passthrough after mirror failure
Coffeeri Jul 23, 2026
4e7520b
vmm: add vm.disk-mirror-cancel REST endpoint
Coffeeri Jun 11, 2026
dcda76a
block: preserve sparseness in CopyWorker
Coffeeri Jun 17, 2026
08a9267
block: add mirror unit tests
Coffeeri Jun 22, 2026
d255754
block: test range guard held across mirror write
Coffeeri Jun 23, 2026
be15b87
virtio-devices, block: reject mirror ops on a paused device
Coffeeri Jun 23, 2026
af2c8ac
vmm: reject mirror destination already backing a disk
Coffeeri Jun 24, 2026
1345f64
vmm: seccomp: allow io_uring and eventfd2 on vcpus
Coffeeri Jun 24, 2026
0698215
block: test batched submit on partial failure
Coffeeri Jun 24, 2026
3020d69
block: test mirror phase transitions and op fan-out
Coffeeri Jun 24, 2026
0fececb
docs: add disk mirroring guide
Coffeeri Jun 25, 2026
e200e1f
vmm: dedup block device lookup in DeviceManager
Coffeeri Jul 6, 2026
b605c6a
virtio-devices: generalize lock granularity
Coffeeri Jul 13, 2026
e75f421
virtio-devices: generalize disk locking
Coffeeri Jul 13, 2026
f521887
virtio-devices, vmm: lock mirror destination
Coffeeri Jul 13, 2026
f271514
block: release stale QEMU lock after downgrade
Coffeeri Jul 20, 2026
c2142b4
block: reject QCOW2 backing chains for mirroring
Coffeeri Jul 20, 2026
0d089e7
vmm: generalize postponed lifecycle events
Coffeeri Jul 21, 2026
10dddd4
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