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
8c84c57
block: add mirror module skeleton
Coffeeri Apr 27, 2026
cba7903
block: add MirroringAsyncIo skeleton
Coffeeri Apr 27, 2026
4bbb86d
block: add range lock primitive for mirror
Coffeeri Apr 28, 2026
c970d7a
block: add CompletionWaiter for single-fd waits
Coffeeri Apr 29, 2026
c393f1f
block: mirror mutating I/O to destination
Coffeeri Apr 28, 2026
769f90a
block: add background copy worker for mirror
Coffeeri Apr 29, 2026
358604b
virtio-devices: swap disk_image via queue commands
Coffeeri Apr 29, 2026
898451e
virtio-devices: pre-allocate per-queue command slots
Coffeeri Apr 29, 2026
776a2a6
block: add BlockMirrorHandle
Coffeeri Apr 29, 2026
af44eb5
block: add MirroringAsyncIo::create
Coffeeri Apr 30, 2026
f6a7563
virtio-devices: add Block::start_mirror
Coffeeri Apr 30, 2026
481aac7
virtio-devices, block: add mirror status helper
Coffeeri Apr 30, 2026
8d79434
vmm: add device manager block mirror start and status
Coffeeri Apr 30, 2026
79b310a
vmm: add vm.disk-mirror-start REST endpoint
Coffeeri Apr 30, 2026
ea23593
vmm: add vm.disk-mirror-status REST endpoint
Coffeeri Apr 30, 2026
ac7659d
block: implement MirroringAsyncIo::submit_batch_requests
Coffeeri Apr 30, 2026
bf4e26e
virtio-devices: drain mirror wrapper before disk_image swap
Coffeeri May 3, 2026
619069a
virtio-devices, block: add Block::complete_mirror
Coffeeri May 3, 2026
d6ec1c7
vmm: add vm.disk-mirror-complete REST endpoint
Coffeeri May 3, 2026
582ccb6
virtio-devices, block: add Block::cancel_mirror
Coffeeri Jun 10, 2026
49194b4
vmm: deny conflicting ops while a disk mirror runs
Coffeeri Jun 10, 2026
de6af7a
block: use source passthrough after mirror failure
Coffeeri Jul 23, 2026
1dba85b
vmm: add vm.disk-mirror-cancel REST endpoint
Coffeeri Jun 11, 2026
88c7c17
block: preserve sparseness in CopyWorker
Coffeeri Jun 17, 2026
d1e66ac
block: add mirror unit tests
Coffeeri Jun 22, 2026
1242989
block: test range guard held across mirror write
Coffeeri Jun 23, 2026
9cd238b
virtio-devices, block: reject mirror ops on a paused device
Coffeeri Jun 23, 2026
286e3c6
vmm: reject mirror destination already backing a disk
Coffeeri Jun 24, 2026
c4f7873
vmm: seccomp: allow io_uring and eventfd2 on vcpus
Coffeeri Jun 24, 2026
8f7fa90
block: test batched submit on partial failure
Coffeeri Jun 24, 2026
35f374b
block: test mirror phase transitions and op fan-out
Coffeeri Jun 24, 2026
0acd82e
docs: add disk mirroring guide
Coffeeri Jun 25, 2026
f1db911
vmm: dedup block device lookup in DeviceManager
Coffeeri Jul 6, 2026
df6a2cd
virtio-devices: generalize lock granularity
Coffeeri Jul 13, 2026
5803525
virtio-devices: generalize disk locking
Coffeeri Jul 13, 2026
af83d2b
virtio-devices, vmm: lock mirror destination
Coffeeri Jul 13, 2026
2cf87de
block: release stale QEMU lock after downgrade
Coffeeri Jul 20, 2026
c680df4
block: reject QCOW2 backing chains for mirroring
Coffeeri Jul 20, 2026
aeee3ce
vmm: generalize postponed lifecycle events
Coffeeri Jul 21, 2026
6b3083f
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