vmm: config: Expose disk lock granularity option

Add a per-disk lock_granularity parameter that lets users choose
between byte-range OFD locks and whole-file OFD locks:

  --disk path=/foo.img,lock_granularity=byte-range
  --disk path=/bar.img,lock_granularity=full

Byte-range is the default and matches QEMU behavior, working
best with storage backends where whole-file OFD locks are treated
as mandatory. The full option restores the original whole-file
locking for environments that depend on it.

The LockGranularityChoice enum and its FromStr impl live in the
block crate alongside the existing LockGranularity type. The
Block device converts the user-facing choice to the internal
LockGranularity at lock time, keeping device_manager.rs simple.

Closes: #7553

Signed-off-by: Victor Vieux <vieux@repl.it>
This commit is contained in:
Victor Vieux
2026-03-06 12:09:09 -08:00
committed by Rob Bradford
parent da0d0a2090
commit 7c690ffec0
7 changed files with 91 additions and 20 deletions

View File

@@ -980,7 +980,10 @@ components:
image_type:
type: string
enum: [FixedVhd, Qcow2, Raw, Vhdx, Unknown]
lock_granularity:
type: string
enum: [byte-range, full]
default: byte-range
NetConfig:
type: object

View File

@@ -1159,7 +1159,7 @@ impl DiskConfig {
id=<device_id>,pci_segment=<segment_id>,rate_limit_group=<group_id>,\
queue_affinity=<list_of_queue_indices_with_their_associated_cpuset>,\
serial=<serial_number>,backing_files=on|off,sparse=on|off,\
image_type=<raw,qcow2,vhd,vhdx>";
image_type=<raw,qcow2,vhd,vhdx>,lock_granularity=byte-range|full";
pub fn parse(disk: &str) -> Result<Self> {
let mut parser = OptionParser::new();
@@ -1187,7 +1187,8 @@ impl DiskConfig {
.add("queue_affinity")
.add("backing_files")
.add("sparse")
.add("image_type");
.add("image_type")
.add("lock_granularity");
parser.parse(disk).map_err(Error::ParseDisk)?;
@@ -1289,6 +1290,11 @@ impl DiskConfig {
ImageType::Unknown
};
let lock_granularity = parser
.convert::<LockGranularityChoice>("lock_granularity")
.map_err(Error::ParseDisk)?
.unwrap_or_default();
let bw_tb_config = if bw_size != 0 && bw_refill_time != 0 {
Some(TokenBucketConfig {
size: bw_size,
@@ -1341,6 +1347,7 @@ impl DiskConfig {
backing_files,
sparse,
image_type,
lock_granularity,
})
}
@@ -3800,6 +3807,7 @@ mod unit_tests {
backing_files: false,
sparse: true,
image_type: ImageType::Unknown,
lock_granularity: LockGranularityChoice::default(),
}
}
@@ -3871,6 +3879,20 @@ mod unit_tests {
..disk_fixture()
}
);
assert_eq!(
DiskConfig::parse("path=/path/to_file,lock_granularity=full")?,
DiskConfig {
lock_granularity: LockGranularityChoice::Full,
..disk_fixture()
}
);
assert_eq!(
DiskConfig::parse("path=/path/to_file,lock_granularity=byte-range")?,
DiskConfig {
lock_granularity: LockGranularityChoice::ByteRange,
..disk_fixture()
}
);
assert_eq!(
DiskConfig::parse("path=/path/to_file,queue_affinity=[0@[1],1@[2],2@[3,4],3@[5-8]]")?,
DiskConfig {

View File

@@ -2853,6 +2853,7 @@ impl DeviceManager {
queue_affinity,
disk_cfg.sparse,
disable_sector0_writes,
disk_cfg.lock_granularity,
)
.map_err(DeviceManagerError::CreateVirtioBlock)?;

View File

@@ -9,6 +9,7 @@ use std::str::FromStr;
use std::{fs, result};
use block::ImageType;
pub use block::fcntl::LockGranularityChoice;
use log::{debug, warn};
use net_util::MacAddr;
use serde::{Deserialize, Serialize};
@@ -302,6 +303,8 @@ pub struct DiskConfig {
pub sparse: bool,
#[serde(default)]
pub image_type: ImageType,
#[serde(default)]
pub lock_granularity: LockGranularityChoice,
}
impl ApplyLandlock for DiskConfig {