diff --git a/src/firecracker/swagger/firecracker.yaml b/src/firecracker/swagger/firecracker.yaml index f1fb275ce7f..3c937760672 100644 --- a/src/firecracker/swagger/firecracker.yaml +++ b/src/firecracker/swagger/firecracker.yaml @@ -1238,6 +1238,12 @@ definitions: description: Is block read only. This field is required for virtio-block config and should be omitted for vhost-user-block configuration. + threaded: + type: boolean + description: + Whether to process requests on a dedicated worker thread. + This field is supported only for virtio-block configuration. + default: false path_on_host: type: string description: diff --git a/src/vmm/src/builder.rs b/src/vmm/src/builder.rs index 31226d6539d..abc44aa6714 100644 --- a/src/vmm/src/builder.rs +++ b/src/vmm/src/builder.rs @@ -905,6 +905,7 @@ pub(crate) mod tests { cache_type: custom_block_cfg.cache_type, is_read_only: Some(custom_block_cfg.is_read_only), + threaded: false, path_on_host: Some( block_files .last() diff --git a/src/vmm/src/device_manager/mod.rs b/src/vmm/src/device_manager/mod.rs index d48ae07191d..934361e95ce 100644 --- a/src/vmm/src/device_manager/mod.rs +++ b/src/vmm/src/device_manager/mod.rs @@ -921,6 +921,7 @@ pub(crate) mod tests { is_root_device: is_root, cache_type: CacheType::Unsafe, is_read_only: Some(false), + threaded: false, path_on_host: Some(f.as_path().to_str().unwrap().to_string()), rate_limiter: None, file_engine_type: None, diff --git a/src/vmm/src/device_manager/pci_mngr.rs b/src/vmm/src/device_manager/pci_mngr.rs index a9a5e1aa733..60cc1a449dc 100644 --- a/src/vmm/src/device_manager/pci_mngr.rs +++ b/src/vmm/src/device_manager/pci_mngr.rs @@ -840,6 +840,7 @@ mod tests { "is_root_device": true, "cache_type": "Unsafe", "is_read_only": true, + "threaded": false, "path_on_host": "{}", "rate_limiter": null, "io_engine": "Sync", diff --git a/src/vmm/src/device_manager/persist.rs b/src/vmm/src/device_manager/persist.rs index 6da2fe1ec8d..6101ebe8b10 100644 --- a/src/vmm/src/device_manager/persist.rs +++ b/src/vmm/src/device_manager/persist.rs @@ -834,6 +834,7 @@ mod tests { "is_root_device": true, "cache_type": "Unsafe", "is_read_only": true, + "threaded": false, "path_on_host": "{}", "rate_limiter": null, "io_engine": "Sync", diff --git a/src/vmm/src/devices/virtio/block/device.rs b/src/vmm/src/devices/virtio/block/device.rs index c2b573df03f..5da689b2ac4 100644 --- a/src/vmm/src/devices/virtio/block/device.rs +++ b/src/vmm/src/devices/virtio/block/device.rs @@ -9,6 +9,7 @@ use vmm_sys_util::eventfd::EventFd; use super::BlockError; use super::persist::{BlockConstructorArgs, BlockState}; use super::vhost_user::device::{VhostUserBlock, VhostUserBlockConfig}; +use super::virtio::VirtioBlockError; use super::virtio::device::{VirtioBlock, VirtioBlockConfig}; use crate::devices::virtio::ActivateError; use crate::devices::virtio::device::{VirtioDevice, VirtioDeviceType}; @@ -118,10 +119,14 @@ impl Block { pub(crate) fn spawn_worker( &mut self, - _seccomp_filter: Option>, + seccomp_filter: Option>, ) -> Result<(), BlockError> { match self { - Self::Virtio(_) | Self::VhostUser(_) => Ok(()), + Self::Virtio(b) if b.config.threaded => b + .spawn_worker(seccomp_filter.ok_or(BlockError::MissingSeccompFilter)?) + .map_err(BlockError::VirtioBackend), + Self::Virtio(_) => Ok(()), + Self::VhostUser(_) => Ok(()), } } } 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 e3e54efd723..caa1e47d019 100644 --- a/src/vmm/src/devices/virtio/block/vhost_user/device.rs +++ b/src/vmm/src/devices/virtio/block/vhost_user/device.rs @@ -67,6 +67,10 @@ impl TryFrom<&BlockDeviceConfig> for VhostUserBlockConfig { type Error = VhostUserBlockError; fn try_from(value: &BlockDeviceConfig) -> Result { + if value.threaded { + return Err(VhostUserBlockError::Config); + } + if let (Some(socket), None, None, None, None) = ( &value.socket, &value.is_read_only, @@ -97,6 +101,7 @@ impl From for BlockDeviceConfig { cache_type: value.cache_type, is_read_only: None, + threaded: false, path_on_host: None, rate_limiter: None, file_engine_type: None, @@ -428,6 +433,7 @@ mod tests { cache_type: CacheType::Unsafe, is_read_only: None, + threaded: false, path_on_host: None, rate_limiter: None, file_engine_type: None, @@ -443,6 +449,7 @@ mod tests { cache_type: CacheType::Unsafe, is_read_only: Some(true), + threaded: false, path_on_host: Some("path".to_string()), rate_limiter: None, file_engine_type: Some(FileEngineType::Sync), @@ -458,6 +465,7 @@ mod tests { cache_type: CacheType::Unsafe, is_read_only: Some(true), + threaded: false, path_on_host: Some("path".to_string()), rate_limiter: None, file_engine_type: Some(FileEngineType::Sync), diff --git a/src/vmm/src/devices/virtio/block/virtio/device.rs b/src/vmm/src/devices/virtio/block/virtio/device.rs index 62f9eafa7f8..6bd0a65ff3e 100644 --- a/src/vmm/src/devices/virtio/block/virtio/device.rs +++ b/src/vmm/src/devices/virtio/block/virtio/device.rs @@ -188,6 +188,9 @@ pub struct VirtioBlockConfig { /// If set to true, the drive is opened in read-only mode. Otherwise, the /// drive is opened as read-write. pub is_read_only: bool, + /// If set to true, process requests on a dedicated worker thread. + #[serde(default)] + pub threaded: bool, /// Path of the backing file on the host pub path_on_host: String, /// Rate Limiter for I/O operations. @@ -210,6 +213,7 @@ impl TryFrom<&BlockDeviceConfig> for VirtioBlockConfig { cache_type: value.cache_type, is_read_only: value.is_read_only.unwrap_or(false), + threaded: value.threaded, path_on_host: path_on_host.clone(), rate_limiter: value.rate_limiter, file_engine_type: value.file_engine_type.unwrap_or_default(), @@ -229,6 +233,7 @@ impl From for BlockDeviceConfig { cache_type: value.cache_type, is_read_only: Some(value.is_read_only), + threaded: value.threaded, path_on_host: Some(value.path_on_host), rate_limiter: value.rate_limiter, file_engine_type: Some(value.file_engine_type), @@ -832,6 +837,7 @@ mod tests { cache_type: CacheType::Unsafe, is_read_only: Some(true), + threaded: false, path_on_host: Some("path".to_string()), rate_limiter: None, file_engine_type: Default::default(), @@ -847,6 +853,7 @@ mod tests { cache_type: CacheType::Unsafe, is_read_only: None, + threaded: false, path_on_host: None, rate_limiter: None, file_engine_type: Default::default(), @@ -862,6 +869,7 @@ mod tests { cache_type: CacheType::Unsafe, is_read_only: Some(true), + threaded: false, path_on_host: Some("path".to_string()), rate_limiter: None, file_engine_type: Default::default(), @@ -1970,6 +1978,7 @@ mod tests { for threaded in [false, true] { let mut block = default_block(engine); if threaded { + block.config.threaded = true; block.spawn_worker(Arc::new(vec![])).unwrap(); } diff --git a/src/vmm/src/devices/virtio/block/virtio/persist.rs b/src/vmm/src/devices/virtio/block/virtio/persist.rs index 2eae6ea31a6..68435b8f1ce 100644 --- a/src/vmm/src/devices/virtio/block/virtio/persist.rs +++ b/src/vmm/src/devices/virtio/block/virtio/persist.rs @@ -62,6 +62,8 @@ pub struct VirtioBlockState { pub virtio_state: VirtioDeviceState, rate_limiter_state: RateLimiterState, file_engine_type: FileEngineTypeState, + #[serde(default)] + threaded: bool, } impl Persist<'_> for VirtioBlock { @@ -91,6 +93,7 @@ impl Persist<'_> for VirtioBlock { virtio_state, rate_limiter_state: self.rate_limiter().save(), file_engine_type: FileEngineTypeState::from(self.file_engine_type()), + threaded: self.config.threaded, } } @@ -108,6 +111,7 @@ impl Persist<'_> for VirtioBlock { is_root_device: state.root_device, cache_type: state.cache_type, is_read_only, + threaded: state.threaded, path_on_host: state.disk_path.clone(), rate_limiter: rate_limiter_config.into_option(), file_engine_type: state.file_engine_type.into(), @@ -178,6 +182,7 @@ mod tests { is_root_device: false, partuuid: None, is_read_only: false, + threaded: false, cache_type: CacheType::Writeback, rate_limiter: None, file_engine_type: FileEngineType::default(), @@ -219,6 +224,7 @@ mod tests { is_root_device: false, partuuid: None, is_read_only: false, + threaded: false, cache_type: CacheType::Unsafe, rate_limiter: None, file_engine_type: FileEngineType::default(), 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 a9fb456eb30..5a77eec69e2 100644 --- a/src/vmm/src/devices/virtio/block/virtio/test_utils.rs +++ b/src/vmm/src/devices/virtio/block/virtio/test_utils.rs @@ -43,6 +43,7 @@ pub fn default_block_with_path(path: String, file_engine_type: FileEngineType) - is_root_device: false, partuuid: None, is_read_only: false, + threaded: false, cache_type: CacheType::Unsafe, // Rate limiting is enabled but with a high operation rate (10 million ops/s). rate_limiter: Some(RateLimiterConfig { diff --git a/src/vmm/src/resources.rs b/src/vmm/src/resources.rs index dc6c43f57a4..7dbed134b4e 100644 --- a/src/vmm/src/resources.rs +++ b/src/vmm/src/resources.rs @@ -620,6 +620,7 @@ mod tests { cache_type: CacheType::Unsafe, is_read_only: Some(false), + threaded: false, path_on_host: Some(tmp_file.as_path().to_str().unwrap().to_string()), rate_limiter: Some(RateLimiterConfig::default()), file_engine_type: None, diff --git a/src/vmm/src/vmm_config/drive.rs b/src/vmm/src/vmm_config/drive.rs index 2d3fddac830..1d08bfdcad7 100644 --- a/src/vmm/src/vmm_config/drive.rs +++ b/src/vmm/src/vmm_config/drive.rs @@ -51,6 +51,9 @@ pub struct BlockDeviceConfig { /// If set to true, the drive is opened in read-only mode. Otherwise, the /// drive is opened as read-write. pub is_read_only: Option, + /// If set to true, process requests on a dedicated worker thread. + #[serde(default)] + pub threaded: bool, /// Path of the drive. pub path_on_host: Option, /// Rate Limiter for I/O operations. @@ -208,6 +211,7 @@ mod tests { partuuid: self.partuuid.clone(), is_root_device: self.is_root_device, is_read_only: self.is_read_only, + threaded: self.threaded, cache_type: self.cache_type, path_on_host: self.path_on_host.clone(), @@ -237,6 +241,7 @@ mod tests { cache_type: CacheType::Writeback, is_read_only: Some(false), + threaded: false, path_on_host: Some(dummy_path), rate_limiter: None, file_engine_type: None, @@ -271,6 +276,7 @@ mod tests { cache_type: CacheType::Unsafe, is_read_only: Some(true), + threaded: false, path_on_host: Some(dummy_path), rate_limiter: None, file_engine_type: None, @@ -303,6 +309,7 @@ mod tests { cache_type: CacheType::Unsafe, is_read_only: Some(true), + threaded: false, path_on_host: Some(dummy_path), rate_limiter: None, file_engine_type: None, @@ -332,6 +339,7 @@ mod tests { cache_type: CacheType::Unsafe, is_read_only: Some(false), + threaded: false, path_on_host: Some(dummy_path_1), rate_limiter: None, file_engine_type: None, @@ -348,6 +356,7 @@ mod tests { cache_type: CacheType::Unsafe, is_read_only: Some(false), + threaded: false, path_on_host: Some(dummy_path_2), rate_limiter: None, file_engine_type: None, @@ -375,6 +384,7 @@ mod tests { cache_type: CacheType::Unsafe, is_read_only: Some(false), + threaded: false, path_on_host: Some(dummy_path_1), rate_limiter: None, file_engine_type: None, @@ -391,6 +401,7 @@ mod tests { cache_type: CacheType::Unsafe, is_read_only: Some(false), + threaded: false, path_on_host: Some(dummy_path_2), rate_limiter: None, file_engine_type: None, @@ -407,6 +418,7 @@ mod tests { cache_type: CacheType::Unsafe, is_read_only: Some(false), + threaded: false, path_on_host: Some(dummy_path_3), rate_limiter: None, file_engine_type: None, @@ -448,6 +460,7 @@ mod tests { cache_type: CacheType::Unsafe, is_read_only: Some(false), + threaded: false, path_on_host: Some(dummy_path_1), rate_limiter: None, file_engine_type: None, @@ -464,6 +477,7 @@ mod tests { cache_type: CacheType::Unsafe, is_read_only: Some(false), + threaded: false, path_on_host: Some(dummy_path_2), rate_limiter: None, file_engine_type: None, @@ -480,6 +494,7 @@ mod tests { cache_type: CacheType::Unsafe, is_read_only: Some(false), + threaded: false, path_on_host: Some(dummy_path_3), rate_limiter: None, file_engine_type: None, @@ -522,6 +537,7 @@ mod tests { cache_type: CacheType::Unsafe, is_read_only: Some(false), + threaded: false, path_on_host: Some(dummy_path_1.clone()), rate_limiter: None, file_engine_type: None, @@ -538,6 +554,7 @@ mod tests { cache_type: CacheType::Unsafe, is_read_only: Some(false), + threaded: false, path_on_host: Some(dummy_path_2.clone()), rate_limiter: None, file_engine_type: None, @@ -610,6 +627,7 @@ mod tests { cache_type: CacheType::Unsafe, is_read_only: Some(false), + threaded: false, path_on_host: Some(dummy_path_1), rate_limiter: None, file_engine_type: None, @@ -626,6 +644,7 @@ mod tests { cache_type: CacheType::Unsafe, is_read_only: Some(false), + threaded: false, path_on_host: Some(dummy_path_2), rate_limiter: None, file_engine_type: None, @@ -652,6 +671,7 @@ mod tests { cache_type: CacheType::Unsafe, is_read_only: Some(true), + threaded: false, path_on_host: Some(dummy_file.as_path().to_str().unwrap().to_string()), rate_limiter: None, file_engine_type: Some(FileEngineType::Sync), @@ -682,6 +702,7 @@ mod tests { cache_type: CacheType::default(), is_read_only: Some(true), + threaded: false, path_on_host: Some(backing_file.as_path().to_str().unwrap().to_string()), rate_limiter: None, file_engine_type: None, diff --git a/src/vmm/tests/integration_tests.rs b/src/vmm/tests/integration_tests.rs index 444c3d919a9..4193f7acfa5 100644 --- a/src/vmm/tests/integration_tests.rs +++ b/src/vmm/tests/integration_tests.rs @@ -426,6 +426,7 @@ fn test_preboot_load_snap_disallowed_after_boot_resources() { cache_type: CacheType::Unsafe, is_read_only: Some(false), + threaded: false, path_on_host: Some(tmp_file), rate_limiter: None, file_engine_type: None, diff --git a/tests/framework/microvm.py b/tests/framework/microvm.py index 56f1d74887d..d3c99530dcd 100644 --- a/tests/framework/microvm.py +++ b/tests/framework/microvm.py @@ -913,6 +913,7 @@ def add_drive( partuuid=None, cache_type=None, io_engine=None, + threaded=None, ): """Add a block device.""" @@ -925,6 +926,7 @@ def add_drive( partuuid=partuuid, cache_type=cache_type, io_engine=io_engine, + threaded=threaded, ) self.disks[drive_id] = path_on_host diff --git a/tests/framework/vm_config.json b/tests/framework/vm_config.json index b2bac4066d5..82db710b2ed 100644 --- a/tests/framework/vm_config.json +++ b/tests/framework/vm_config.json @@ -11,6 +11,7 @@ "is_root_device": true, "cache_type": "Unsafe", "is_read_only": false, + "threaded": false, "path_on_host": "bionic.rootfs.ext4", "io_engine": "Sync", "rate_limiter": null, diff --git a/tests/integration_tests/functional/test_api.py b/tests/integration_tests/functional/test_api.py index 4efca607133..4cadeacbb52 100644 --- a/tests/integration_tests/functional/test_api.py +++ b/tests/integration_tests/functional/test_api.py @@ -757,6 +757,7 @@ def test_drive_patch(uvm, io_engine): is_root_device=False, is_read_only=False, io_engine=io_engine, + threaded=True, ) fs_vub = drive_tools.FilesystemFile( @@ -906,6 +907,7 @@ def _drive_patch(test_microvm, io_engine): "is_root_device": True, "cache_type": "Unsafe", "is_read_only": True, + "threaded": False, "path_on_host": "/" + test_microvm.rootfs_file.name, "rate_limiter": None, "io_engine": "Sync", @@ -917,6 +919,7 @@ def _drive_patch(test_microvm, io_engine): "is_root_device": False, "cache_type": "Unsafe", "is_read_only": False, + "threaded": True, "path_on_host": "/scratch_new.ext4", "rate_limiter": { "bandwidth": {"size": 5000, "one_time_burst": None, "refill_time": 100}, @@ -931,6 +934,7 @@ def _drive_patch(test_microvm, io_engine): "is_root_device": False, "cache_type": "Unsafe", "is_read_only": None, + "threaded": False, "path_on_host": None, "rate_limiter": None, "io_engine": None, @@ -1320,6 +1324,7 @@ def test_get_full_config_after_restoring_snapshot(microvm_factory, uvm_configure "is_root_device": True, "cache_type": "Unsafe", "is_read_only": True, + "threaded": False, "path_on_host": f"/{uvm_configured.rootfs_file.name}", "rate_limiter": None, "io_engine": "Sync", @@ -1460,6 +1465,7 @@ def test_get_full_config(uvm): "is_root_device": True, "cache_type": "Unsafe", "is_read_only": True, + "threaded": False, "path_on_host": "/" + test_microvm.rootfs_file.name, "rate_limiter": None, "io_engine": "Sync",