diff --git a/src/vmm/src/devices/virtio/block/vhost_user/device.rs b/src/vmm/src/devices/virtio/block/vhost_user/device.rs index dd08b8de7c8..0322af034d4 100644 --- a/src/vmm/src/devices/virtio/block/vhost_user/device.rs +++ b/src/vmm/src/devices/virtio/block/vhost_user/device.rs @@ -74,6 +74,7 @@ impl TryFrom<&BlockDeviceConfig> for VhostUserBlockConfig { && value.path_on_host.is_none() && value.rate_limiter.is_none() && value.file_engine_type.is_none() + && value.is_direct.is_none() { Ok(Self { drive_id: value.drive_id.clone(), @@ -101,6 +102,7 @@ impl From for BlockDeviceConfig { path_on_host: None, rate_limiter: None, file_engine_type: None, + is_direct: None, socket: Some(value.socket), } @@ -405,6 +407,7 @@ mod tests { path_on_host: None, rate_limiter: None, file_engine_type: None, + is_direct: None, socket: Some("sock".to_string()), }; @@ -420,6 +423,7 @@ mod tests { path_on_host: Some("path".to_string()), rate_limiter: None, file_engine_type: Some(FileEngineType::Sync), + is_direct: Some(true), socket: None, }; @@ -435,6 +439,7 @@ mod tests { path_on_host: Some("path".to_string()), rate_limiter: None, file_engine_type: Some(FileEngineType::Sync), + is_direct: Some(true), socket: Some("sock".to_string()), }; diff --git a/src/vmm/src/devices/virtio/block/virtio/device.rs b/src/vmm/src/devices/virtio/block/virtio/device.rs index ecdd8ee4f6d..2b26ae54310 100644 --- a/src/vmm/src/devices/virtio/block/virtio/device.rs +++ b/src/vmm/src/devices/virtio/block/virtio/device.rs @@ -11,6 +11,7 @@ use std::fs::{File, OpenOptions}; use std::io::{Seek, SeekFrom}; use std::ops::Deref; use std::os::linux::fs::MetadataExt; +use std::os::unix::fs::OpenOptionsExt; use std::path::PathBuf; use std::sync::Arc; @@ -63,10 +64,19 @@ pub struct DiskProperties { impl DiskProperties { // Helper function that opens the file with the proper access permissions - fn open_file(disk_image_path: &str, is_disk_read_only: bool) -> Result { - OpenOptions::new() - .read(true) - .write(!is_disk_read_only) + fn open_file( + disk_image_path: &str, + is_disk_read_only: bool, + is_direct: bool, + ) -> Result { + let mut options = OpenOptions::new(); + options.read(true).write(!is_disk_read_only); + + if is_direct { + options.custom_flags(libc::O_DIRECT); + } + + options .open(PathBuf::from(&disk_image_path)) .map_err(|x| VirtioBlockError::BackingFile(x, disk_image_path.to_string())) } @@ -95,8 +105,9 @@ impl DiskProperties { disk_image_path: String, is_disk_read_only: bool, file_engine_type: FileEngineType, + is_direct: bool, ) -> Result { - let mut disk_image = Self::open_file(&disk_image_path, is_disk_read_only)?; + let mut disk_image = Self::open_file(&disk_image_path, is_disk_read_only, is_direct)?; let disk_size = Self::file_size(&disk_image_path, &mut disk_image)?; let image_id = Self::build_disk_image_id(&disk_image); @@ -114,8 +125,9 @@ impl DiskProperties { &mut self, disk_image_path: String, is_disk_read_only: bool, + is_direct: bool, ) -> Result<(), VirtioBlockError> { - let mut disk_image = Self::open_file(&disk_image_path, is_disk_read_only)?; + let mut disk_image = Self::open_file(&disk_image_path, is_disk_read_only, is_direct)?; let disk_size = Self::file_size(&disk_image_path, &mut disk_image)?; self.image_id = Self::build_disk_image_id(&disk_image); @@ -198,6 +210,8 @@ pub struct VirtioBlockConfig { #[serde(default)] #[serde(rename = "io_engine")] pub file_engine_type: FileEngineType, + /// If set to true, the drive is opened with O_DIRECT. + pub is_direct: bool, } impl TryFrom<&BlockDeviceConfig> for VirtioBlockConfig { @@ -215,6 +229,7 @@ impl TryFrom<&BlockDeviceConfig> for VirtioBlockConfig { path_on_host: value.path_on_host.as_ref().unwrap().clone(), rate_limiter: value.rate_limiter, file_engine_type: value.file_engine_type.unwrap_or_default(), + is_direct: value.is_direct.unwrap_or(false), }) } else { Err(VirtioBlockError::Config) @@ -234,6 +249,7 @@ impl From for BlockDeviceConfig { path_on_host: Some(value.path_on_host), rate_limiter: value.rate_limiter, file_engine_type: Some(value.file_engine_type), + is_direct: Some(value.is_direct), socket: None, } @@ -260,6 +276,7 @@ pub struct VirtioBlock { pub cache_type: CacheType, pub root_device: bool, pub read_only: bool, + pub direct: bool, // Host file and properties. pub disk: DiskProperties, @@ -289,6 +306,7 @@ impl VirtioBlock { config.path_on_host, config.is_read_only, config.file_engine_type, + config.is_direct, )?; let rate_limiter = config @@ -331,6 +349,7 @@ impl VirtioBlock { cache_type: config.cache_type, root_device: config.is_root_device, read_only: config.is_read_only, + direct: config.is_direct, disk: disk_properties, rate_limiter, @@ -351,6 +370,7 @@ impl VirtioBlock { cache_type: self.cache_type, rate_limiter: rl.into_option(), file_engine_type: self.file_engine_type(), + is_direct: self.direct, } } @@ -536,7 +556,8 @@ impl VirtioBlock { /// Update the backing file and the config space of the block device. pub fn update_disk_image(&mut self, disk_image_path: String) -> Result<(), VirtioBlockError> { - self.disk.update(disk_image_path, self.read_only)?; + self.disk + .update(disk_image_path, self.read_only, self.direct)?; self.config_space.capacity = self.disk.nsectors.to_le(); // virtio_block_config_space(); // Kick the driver to pick up the changes. (But only if the device is already activated). @@ -723,6 +744,7 @@ mod tests { path_on_host: Some("path".to_string()), rate_limiter: None, file_engine_type: Default::default(), + is_direct: Some(true), socket: None, }; @@ -738,6 +760,7 @@ mod tests { path_on_host: None, rate_limiter: None, file_engine_type: Default::default(), + is_direct: None, socket: Some("sock".to_string()), }; @@ -753,6 +776,7 @@ mod tests { path_on_host: Some("path".to_string()), rate_limiter: None, file_engine_type: Default::default(), + is_direct: Some(true), socket: Some("sock".to_string()), }; @@ -767,16 +791,20 @@ mod tests { f.as_file().set_len(size).unwrap(); for engine in [FileEngineType::Sync, FileEngineType::Async] { - let disk_properties = - DiskProperties::new(String::from(f.as_path().to_str().unwrap()), true, engine) - .unwrap(); + let disk_properties = DiskProperties::new( + String::from(f.as_path().to_str().unwrap()), + true, + engine, + true, + ) + .unwrap(); assert_eq!(size, u64::from(SECTOR_SIZE) * num_sectors); assert_eq!(disk_properties.nsectors, num_sectors); // Testing `backing_file.virtio_block_disk_image_id()` implies // duplicating that logic in tests, so skipping it. - let res = DiskProperties::new("invalid-disk-path".to_string(), true, engine); + let res = DiskProperties::new("invalid-disk-path".to_string(), true, engine, true); assert!( matches!(res, Err(VirtioBlockError::BackingFile(_, _))), "{:?}", diff --git a/src/vmm/src/devices/virtio/block/virtio/persist.rs b/src/vmm/src/devices/virtio/block/virtio/persist.rs index 380fe1de0e8..36258ec90ba 100644 --- a/src/vmm/src/devices/virtio/block/virtio/persist.rs +++ b/src/vmm/src/devices/virtio/block/virtio/persist.rs @@ -61,6 +61,7 @@ pub struct VirtioBlockState { pub virtio_state: VirtioDeviceState, rate_limiter_state: RateLimiterState, file_engine_type: FileEngineTypeState, + direct: Option, } impl Persist<'_> for VirtioBlock { @@ -79,6 +80,7 @@ impl Persist<'_> for VirtioBlock { virtio_state: VirtioDeviceState::from_device(self), rate_limiter_state: self.rate_limiter.save(), file_engine_type: FileEngineTypeState::from(self.file_engine_type()), + direct: Some(self.direct), } } @@ -89,11 +91,13 @@ impl Persist<'_> for VirtioBlock { let is_read_only = state.virtio_state.avail_features & (1u64 << VIRTIO_BLK_F_RO) != 0; let rate_limiter = RateLimiter::restore((), &state.rate_limiter_state) .map_err(VirtioBlockError::RateLimiter)?; + let is_direct = state.direct.unwrap_or(false); let disk_properties = DiskProperties::new( state.disk_path.clone(), is_read_only, state.file_engine_type.into(), + is_direct, )?; let queue_evts = [EventFd::new(libc::EFD_NONBLOCK).map_err(VirtioBlockError::EventFd)?]; @@ -130,6 +134,7 @@ impl Persist<'_> for VirtioBlock { cache_type: state.cache_type, root_device: state.root_device, read_only: is_read_only, + direct: is_direct, disk: disk_properties, rate_limiter, @@ -164,6 +169,7 @@ mod tests { cache_type: CacheType::Writeback, rate_limiter: None, file_engine_type: FileEngineType::default(), + is_direct: false, }; let block = VirtioBlock::new(config).unwrap(); @@ -208,6 +214,7 @@ mod tests { cache_type: CacheType::Unsafe, rate_limiter: None, file_engine_type: FileEngineType::default(), + is_direct: false, }; let block = VirtioBlock::new(config).unwrap(); diff --git a/src/vmm/src/devices/virtio/block/virtio/test_utils.rs b/src/vmm/src/devices/virtio/block/virtio/test_utils.rs index e4f23c6a038..a36b8632f22 100644 --- a/src/vmm/src/devices/virtio/block/virtio/test_utils.rs +++ b/src/vmm/src/devices/virtio/block/virtio/test_utils.rs @@ -58,6 +58,7 @@ pub fn default_block_with_path(path: String, file_engine_type: FileEngineType) - }), }), file_engine_type, + is_direct: false, }; // The default block device is read-write and non-root. diff --git a/src/vmm/src/vmm_config/drive.rs b/src/vmm/src/vmm_config/drive.rs index adf8083f74d..c80aee47356 100644 --- a/src/vmm/src/vmm_config/drive.rs +++ b/src/vmm/src/vmm_config/drive.rs @@ -60,6 +60,8 @@ pub struct BlockDeviceConfig { // pub file_engine_type: FileEngineType, #[serde(rename = "io_engine")] pub file_engine_type: Option, + /// If set to true, the drive is opened with O_DIRECT. + pub is_direct: Option, // VhostUserBlock specific fields /// Path to the vhost-user socket.