Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
edcea69
block: add mirror module skeleton
Coffeeri Apr 27, 2026
a465173
block: add MirroringAsyncIo skeleton
Coffeeri Apr 27, 2026
10537da
block: add range lock primitive for mirror
Coffeeri Apr 28, 2026
80b6c30
block: add CompletionIo for single-fd waits
Coffeeri Apr 29, 2026
930bee7
block: mirror mutating I/O to destination
Coffeeri Apr 28, 2026
6993515
block: add background copy worker for mirror
Coffeeri Apr 29, 2026
4ae7d5a
virtio-devices: swap disk_image via queue commands
Coffeeri Apr 29, 2026
a1031cb
virtio-devices: set up mirror command channels
Coffeeri Apr 29, 2026
ef9d939
virtio-devices, block: add Block::start_mirror
Coffeeri Apr 30, 2026
666b7b9
vmm: add device manager block mirror start and status
Coffeeri Apr 30, 2026
19eb5be
vmm: add vm.disk-mirror-start REST endpoint
Coffeeri Apr 30, 2026
cafe346
vmm: add vm.disk-mirror-status REST endpoint
Coffeeri Apr 30, 2026
a8cf5df
block: implement MirroringAsyncIo::submit_batch_requests
Coffeeri Apr 30, 2026
dd72917
virtio-devices, block: add Block::complete_mirror
Coffeeri May 3, 2026
3e99823
vmm: add vm.disk-mirror-complete REST endpoint
Coffeeri May 3, 2026
c2665e3
virtio-devices, block: add Block::cancel_mirror
Coffeeri Jun 10, 2026
8a96e9b
vmm: deny conflicting ops while a disk mirror runs
Coffeeri Jun 10, 2026
eeb1953
block: use source passthrough after mirror failure
Coffeeri Jul 23, 2026
b8481ad
vmm: add vm.disk-mirror-cancel REST endpoint
Coffeeri Jun 11, 2026
43d9e70
block: preserve sparseness in CopyWorker
Coffeeri Jun 17, 2026
4f56164
block: add mirror unit tests
Coffeeri Jun 22, 2026
3429600
block: test range guard held across mirror write
Coffeeri Jun 23, 2026
860e398
virtio-devices, block: reject mirror ops on a paused device
Coffeeri Jun 23, 2026
a5d9519
vmm: reject mirror destination already backing a disk
Coffeeri Jun 24, 2026
62f61b7
block: test batched submit on partial failure
Coffeeri Jun 24, 2026
ebd9372
block: test mirror phase transitions and op fan-out
Coffeeri Jun 24, 2026
a088046
docs: add disk mirroring guide
Coffeeri Jun 25, 2026
5ccc370
vmm: dedup block device lookup in DeviceManager
Coffeeri Jul 6, 2026
700aba0
virtio-devices: generalize lock granularity
Coffeeri Jul 13, 2026
86c5022
virtio-devices: generalize disk locking
Coffeeri Jul 13, 2026
db03820
virtio-devices, vmm: lock mirror destination
Coffeeri Jul 13, 2026
7dabb3f
block: release stale QEMU lock after downgrade
Coffeeri Jul 20, 2026
d153397
block: reject QCOW2 backing chains for mirroring
Coffeeri Jul 20, 2026
f91d9f7
vmm: generalize postponed lifecycle events
Coffeeri Jul 21, 2026
9e457c6
vmm: postpone guest lifecycle for disk mirrors
Coffeeri Jul 21, 2026
ded51be
virtio-devices, block: emit disk mirror events
Coffeeri Aug 12, 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
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions block/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ io_uring = ["dep:io-uring"]
bitflags = { workspace = true }
byteorder = { workspace = true }
crc-any = "2.5.0"
event_monitor = { path = "../event_monitor" }
flate2 = "1.1"
io-uring = { version = "0.7.12", optional = true }
libc = { workspace = true }
Expand Down
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