From 8fb7ab75186b3077e556c566e2beb951571c4035 Mon Sep 17 00:00:00 2001 From: Helin Guo Date: Sun, 29 Jan 2023 16:18:06 +0800 Subject: [PATCH] dragonball: introduce virtio-balloon device We introduce virtio-balloon device to support memory resize. virtio-balloon device could reclaim memory from guest to host. Fixes: #6719 Signed-off-by: Helin Guo --- src/dragonball/Cargo.lock | 124 ++++-- src/dragonball/Cargo.toml | 1 + src/dragonball/src/api/v1/vmm_action.rs | 87 +++- .../src/device_manager/balloon_dev_mgr.rs | 419 ++++++++++++++++++ src/dragonball/src/device_manager/mod.rs | 13 + src/dragonball/src/error.rs | 5 + src/dragonball/src/vm/mod.rs | 13 + 7 files changed, 632 insertions(+), 30 deletions(-) create mode 100644 src/dragonball/src/device_manager/balloon_dev_mgr.rs diff --git a/src/dragonball/Cargo.lock b/src/dragonball/Cargo.lock index 3cc177fe1cf3..0ed990c0becd 100644 --- a/src/dragonball/Cargo.lock +++ b/src/dragonball/Cargo.lock @@ -180,9 +180,9 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.7" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf2b3e8478797446514c91ef04bafcb59faba183e621ad488df88983cc14128c" +checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" dependencies = [ "cfg-if", "crossbeam-utils", @@ -445,13 +445,13 @@ dependencies = [ [[package]] name = "errno" -version = "0.2.8" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" +checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" dependencies = [ "errno-dragonfly", "libc", - "winapi", + "windows-sys 0.48.0", ] [[package]] @@ -483,7 +483,7 @@ dependencies = [ "cfg-if", "libc", "redox_syscall", - "windows-sys", + "windows-sys 0.45.0", ] [[package]] @@ -619,9 +619,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" +checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" dependencies = [ "cfg-if", "libc", @@ -671,7 +671,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1abeb7a0dd0f8181267ff8adc397075586500b81b28a73e8a0208b00fc170fb3" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.45.0", ] [[package]] @@ -842,7 +842,7 @@ dependencies = [ "libc", "log", "wasi", - "windows-sys", + "windows-sys 0.45.0", ] [[package]] @@ -1045,7 +1045,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-sys", + "windows-sys 0.45.0", ] [[package]] @@ -1121,16 +1121,16 @@ checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" [[package]] name = "rustix" -version = "0.36.8" +version = "0.36.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f43abb88211988493c1abb44a70efa56ff0ce98f233b7b276146f1f3f7ba9644" +checksum = "14e4d67015953998ad0eb82887a0eb0129e18a7e2f3b7b0f6c422fddcd503d62" dependencies = [ "bitflags", "errno", "io-lifetimes", "libc", "linux-raw-sys", - "windows-sys", + "windows-sys 0.45.0", ] [[package]] @@ -1168,18 +1168,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.152" +version = "1.0.156" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" +checksum = "314b5b092c0ade17c00142951e50ced110ec27cea304b1037c6969246c2469a4" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.152" +version = "1.0.156" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" +checksum = "d7e29c4601e36bcec74a223228dce795f4cd3616341a4af93520ca1a837c087d" dependencies = [ "proc-macro2", "quote", @@ -1188,9 +1188,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.93" +version = "1.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cad406b69c91885b5107daf2c29572f6c8cdb3c66826821e286c533490c0bc76" +checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" dependencies = [ "itoa", "ryu", @@ -1423,7 +1423,7 @@ dependencies = [ "pin-project-lite", "socket2", "tokio-macros", - "windows-sys", + "windows-sys 0.45.0", ] [[package]] @@ -1611,7 +1611,16 @@ version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" dependencies = [ - "windows-targets", + "windows-targets 0.42.1", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.0", ] [[package]] @@ -1620,13 +1629,28 @@ version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.42.1", + "windows_aarch64_msvc 0.42.1", + "windows_i686_gnu 0.42.1", + "windows_i686_msvc 0.42.1", + "windows_x86_64_gnu 0.42.1", + "windows_x86_64_gnullvm 0.42.1", + "windows_x86_64_msvc 0.42.1", +] + +[[package]] +name = "windows-targets" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +dependencies = [ + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", ] [[package]] @@ -1635,42 +1659,84 @@ version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + [[package]] name = "windows_aarch64_msvc" version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + [[package]] name = "windows_i686_gnu" version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + [[package]] name = "windows_i686_msvc" version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + [[package]] name = "windows_x86_64_gnu" version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + [[package]] name = "windows_x86_64_gnullvm" version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + [[package]] name = "windows_x86_64_msvc" version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" + [[package]] name = "xattr" version = "0.2.3" diff --git a/src/dragonball/Cargo.toml b/src/dragonball/Cargo.toml index 0c94530ca0af..b0e03047d84b 100644 --- a/src/dragonball/Cargo.toml +++ b/src/dragonball/Cargo.toml @@ -56,3 +56,4 @@ virtio-net = ["dbs-virtio-devices/virtio-net", "virtio-queue"] # virtio-fs only work on atomic-guest-memory virtio-fs = ["dbs-virtio-devices/virtio-fs", "virtio-queue", "atomic-guest-memory"] virtio-mem = ["dbs-virtio-devices/virtio-mem", "virtio-queue", "atomic-guest-memory"] +virtio-balloon = ["dbs-virtio-devices/virtio-balloon", "virtio-queue"] diff --git a/src/dragonball/src/api/v1/vmm_action.rs b/src/dragonball/src/api/v1/vmm_action.rs index 6243b82a3340..247daf10d51a 100644 --- a/src/dragonball/src/api/v1/vmm_action.rs +++ b/src/dragonball/src/api/v1/vmm_action.rs @@ -19,6 +19,8 @@ use crate::vmm::Vmm; use self::VmConfigError::*; use self::VmmActionError::MachineConfig; +#[cfg(feature = "virtio-balloon")] +pub use crate::device_manager::balloon_dev_mgr::{BalloonDeviceConfigInfo, BalloonDeviceError}; #[cfg(feature = "virtio-blk")] pub use crate::device_manager::blk_dev_mgr::{ BlockDeviceConfigInfo, BlockDeviceConfigUpdateInfo, BlockDeviceError, BlockDeviceMgr, @@ -36,7 +38,6 @@ pub use crate::device_manager::virtio_net_dev_mgr::{ }; #[cfg(feature = "virtio-vsock")] pub use crate::device_manager::vsock_dev_mgr::{VsockDeviceConfigInfo, VsockDeviceError}; - #[cfg(feature = "hotplug")] pub use crate::vcpu::{VcpuResizeError, VcpuResizeInfo}; @@ -108,6 +109,11 @@ pub enum VmmActionError { /// Mem device related errors. #[error("virtio-mem device error: {0}")] Mem(#[source] MemDeviceError), + + #[cfg(feature = "virtio-balloon")] + /// Balloon device related errors. + #[error("virtio-balloon device error: {0}")] + Balloon(#[source] BalloonDeviceError), } /// This enum represents the public interface of the VMM. Each action contains various @@ -187,6 +193,11 @@ pub enum VmmAction { #[cfg(feature = "virtio-mem")] /// Add a new mem device or update one that already exists using the `MemDeviceConfig` as input. InsertMemDevice(MemDeviceConfigInfo), + + #[cfg(feature = "virtio-balloon")] + /// Add a new balloon device or update one that already exists using the `BalloonDeviceConfig` + /// as input. + InsertBalloonDevice(BalloonDeviceConfigInfo), } /// The enum represents the response sent by the VMM in case of success. The response is either @@ -291,6 +302,10 @@ impl VmmService { VmmAction::ResizeVcpu(vcpu_resize_cfg) => self.resize_vcpu(vmm, vcpu_resize_cfg), #[cfg(feature = "virtio-mem")] VmmAction::InsertMemDevice(mem_cfg) => self.add_mem_device(vmm, event_mgr, mem_cfg), + #[cfg(feature = "virtio-balloon")] + VmmAction::InsertBalloonDevice(balloon_cfg) => { + self.add_balloon_device(vmm, event_mgr, balloon_cfg) + } }; debug!("send vmm response: {:?}", response); @@ -691,6 +706,36 @@ impl VmmService { .map(|_| VmmData::Empty) .map_err(VmmActionError::Mem) } + + #[cfg(feature = "virtio-balloon")] + fn add_balloon_device( + &mut self, + vmm: &mut Vmm, + event_mgr: &mut EventManager, + config: BalloonDeviceConfigInfo, + ) -> VmmRequestResult { + let vm = vmm.get_vm_mut().ok_or(VmmActionError::InvalidVMID)?; + + if config.size_mib != 0 { + info!("add_balloon_device: wait prealloc"); + vm.stop_prealloc().map_err(VmmActionError::StartMicroVm)?; + } + let ctx = vm + .create_device_op_context(Some(event_mgr.epoll_manager())) + .map_err(|e| { + if let StartMicroVmError::UpcallServerNotReady = e { + VmmActionError::UpcallServerNotReady + } else { + VmmActionError::StartMicroVm(e) + } + })?; + + vm.device_manager_mut() + .balloon_manager + .insert_or_update_device(ctx, config) + .map(|_| VmmData::Empty) + .map_err(VmmActionError::Balloon) + } } fn handle_cpu_topology( @@ -1539,4 +1584,44 @@ mod tests { t.check_request(); } } + + #[cfg(feature = "virtio-balloon")] + #[test] + fn test_vmm_action_insert_balloon_device() { + skip_if_not_root!(); + + let tests = &mut [ + // hotplug unready + TestData::new( + VmmAction::InsertBalloonDevice(BalloonDeviceConfigInfo::default()), + InstanceState::Running, + &|result| { + assert!(matches!( + result, + Err(VmmActionError::StartMicroVm( + StartMicroVmError::UpcallMissVsock + )) + )); + let err_string = format!("{}", result.unwrap_err()); + let expected_err = String::from( + "failed to boot the VM: \ + the upcall client needs a virtio-vsock device for communication", + ); + assert_eq!(err_string, expected_err); + }, + ), + // success + TestData::new( + VmmAction::InsertBalloonDevice(BalloonDeviceConfigInfo::default()), + InstanceState::Uninitialized, + &|result| { + assert!(result.is_ok()); + }, + ), + ]; + + for t in tests.iter_mut() { + t.check_request(); + } + } } diff --git a/src/dragonball/src/device_manager/balloon_dev_mgr.rs b/src/dragonball/src/device_manager/balloon_dev_mgr.rs new file mode 100644 index 000000000000..b0ee2bd37e29 --- /dev/null +++ b/src/dragonball/src/device_manager/balloon_dev_mgr.rs @@ -0,0 +1,419 @@ +// Copyright 2020 Alibaba Cloud. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +use dbs_virtio_devices as virtio; +use serde_derive::{Deserialize, Serialize}; +use slog::{error, info}; +use virtio::balloon::{Balloon, BalloonConfig}; +use virtio::Error as VirtIoError; + +use crate::address_space_manager::GuestAddressSpaceImpl; +use crate::config_manager::{ConfigItem, DeviceConfigInfo, DeviceConfigInfos}; +use crate::device_manager::DbsMmioV2Device; +use crate::device_manager::{DeviceManager, DeviceMgrError, DeviceOpContext}; + +// The flag of whether to use the shared irq. +const USE_SHARED_IRQ: bool = true; +// The flag of whether to use the generic irq. +const USE_GENERIC_IRQ: bool = false; + +/// Errors associated with `BalloonDeviceConfig`. +#[derive(Debug, thiserror::Error)] +pub enum BalloonDeviceError { + /// The balloon device was already used. + #[error("the virtio-balloon ID was already added to a different device")] + BalloonDeviceAlreadyExists, + + /// Cannot perform the requested operation after booting the microVM. + #[error("the update operation is not allowed after boot")] + UpdateNotAllowedPostBoot, + + /// guest memory error + #[error("failed to access guest memory, {0}")] + GuestMemoryError(#[source] vm_memory::mmap::Error), + + /// create balloon device error + #[error("failed to create virtio-balloon device, {0}")] + CreateBalloonDevice(#[source] virtio::Error), + + /// hotplug balloon device error + #[error("cannot hotplug virtio-balloon device, {0}")] + HotplugDeviceFailed(#[source] DeviceMgrError), + + /// create mmio device error + #[error("cannot create virtio-balloon mmio device, {0}")] + CreateMmioDevice(#[source] DeviceMgrError), + + /// Cannot initialize a balloon device or add a device to the MMIO Bus. + #[error("failure while registering balloon device: {0}")] + RegisterBalloonDevice(#[source] DeviceMgrError), + + /// resize balloon device error + #[error("failure while resizing virtio-balloon device, {0}")] + ResizeFailed(#[source] VirtIoError), + + /// The balloon device id doesn't exist. + #[error("invalid balloon device id '{0}'")] + InvalidDeviceId(String), + + /// balloon device does not exist + #[error("balloon device does not exist")] + NotExist, + + /// The device manager errors. + #[error("DeviceManager error: {0}")] + DeviceManager(#[source] DeviceMgrError), +} + +/// Configuration information for a virtio-balloon device. +#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)] +pub struct BalloonDeviceConfigInfo { + /// Unique identifier of the balloon device + pub balloon_id: String, + /// Resize balloon size in mib + pub size_mib: u64, + /// Use shared irq + pub use_shared_irq: Option, + /// Use generic irq + pub use_generic_irq: Option, + /// VIRTIO_BALLOON_F_DEFLATE_ON_OOM + pub f_deflate_on_oom: bool, + /// VIRTIO_BALLOON_F_REPORTING + pub f_reporting: bool, +} + +impl ConfigItem for BalloonDeviceConfigInfo { + type Err = BalloonDeviceError; + + fn id(&self) -> &str { + &self.balloon_id + } + + fn check_conflicts(&self, other: &Self) -> Result<(), BalloonDeviceError> { + if self.balloon_id.as_str() == other.balloon_id.as_str() { + Err(BalloonDeviceError::BalloonDeviceAlreadyExists) + } else { + Ok(()) + } + } +} + +/// Balloon Device Info +pub type BalloonDeviceInfo = DeviceConfigInfo; + +impl ConfigItem for BalloonDeviceInfo { + type Err = BalloonDeviceError; + + fn id(&self) -> &str { + &self.config.balloon_id + } + + fn check_conflicts(&self, other: &Self) -> Result<(), BalloonDeviceError> { + if self.config.balloon_id.as_str() == other.config.balloon_id.as_str() { + Err(BalloonDeviceError::BalloonDeviceAlreadyExists) + } else { + Ok(()) + } + } +} + +/// Wrapper for the collection that holds all the Balloon Devices Configs +#[derive(Clone)] +pub struct BalloonDeviceMgr { + /// A list of `BalloonDeviceConfig` objects. + info_list: DeviceConfigInfos, + pub(crate) use_shared_irq: bool, +} + +impl BalloonDeviceMgr { + /// Inserts `balloon_cfg` in the virtio-balloon device configuration list. + /// If an entry with the same id already exists, it will attempt to update + /// the existing entry. + pub fn insert_or_update_device( + &mut self, + mut ctx: DeviceOpContext, + balloon_cfg: BalloonDeviceConfigInfo, + ) -> std::result::Result<(), BalloonDeviceError> { + if !cfg!(feature = "hotplug") && ctx.is_hotplug { + error!(ctx.logger(), "hotplug feature has been disabled."; + "subsystem" => "balloon_dev_mgr",); + return Err(BalloonDeviceError::UpdateNotAllowedPostBoot); + } + + let epoll_mgr = ctx + .get_epoll_mgr() + .map_err(BalloonDeviceError::DeviceManager)?; + + // If the id of the drive already exists in the list, the operation is update. + if let Some(index) = self.get_index_of_balloon_dev(&balloon_cfg.balloon_id) { + // Update an existing balloon device + if ctx.is_hotplug { + info!(ctx.logger(), "resize virtio balloon size to {:?}", balloon_cfg.size_mib; "subsystem" => "balloon_dev_mgr"); + self.update_balloon_size(index, balloon_cfg.size_mib)?; + } + self.info_list.insert_or_update(&balloon_cfg)?; + } else { + // Create a new balloon device + if !self.info_list.is_empty() { + error!(ctx.logger(), "only support one balloon device!"; "subsystem" => "balloon_dev_mgr"); + return Err(BalloonDeviceError::BalloonDeviceAlreadyExists); + } + + if !ctx.is_hotplug { + self.info_list.insert_or_update(&balloon_cfg)?; + return Ok(()); + } + + info!(ctx.logger(), "hotplug balloon device: {}", balloon_cfg.balloon_id; "subsystem" => "balloon_dev_mgr"); + let device = Box::new( + virtio::balloon::Balloon::new( + epoll_mgr, + BalloonConfig { + f_deflate_on_oom: balloon_cfg.f_deflate_on_oom, + f_reporting: balloon_cfg.f_reporting, + }, + ) + .map_err(BalloonDeviceError::CreateBalloonDevice)?, + ); + + let mmio_dev = + DeviceManager::create_mmio_virtio_device_with_device_change_notification( + device, + &mut ctx, + balloon_cfg.use_shared_irq.unwrap_or(self.use_shared_irq), + balloon_cfg.use_generic_irq.unwrap_or(USE_GENERIC_IRQ), + ) + .map_err(BalloonDeviceError::CreateMmioDevice)?; + ctx.insert_hotplug_mmio_device(&mmio_dev, None) + .map_err(|e| { + error!( + ctx.logger(), + "hotplug balloon device {} error: {}", + &balloon_cfg.balloon_id, e; + "subsystem" => "balloon_dev_mgr" + ); + BalloonDeviceError::HotplugDeviceFailed(e) + })?; + let index = self.info_list.insert_or_update(&balloon_cfg)?; + self.info_list[index].set_device(mmio_dev); + } + Ok(()) + } + + /// Attaches all virtio-balloon devices from the BalloonDevicesConfig. + pub fn attach_devices( + &mut self, + ctx: &mut DeviceOpContext, + ) -> std::result::Result<(), BalloonDeviceError> { + let epoll_mgr = ctx + .get_epoll_mgr() + .map_err(BalloonDeviceError::DeviceManager)?; + + for info in self.info_list.iter_mut() { + info!(ctx.logger(), "attach balloon device: {}", info.config.balloon_id; "subsystem" => "balloon_dev_mgr"); + + let device = Balloon::new( + epoll_mgr.clone(), + BalloonConfig { + f_deflate_on_oom: info.config.f_deflate_on_oom, + f_reporting: info.config.f_reporting, + }, + ) + .map_err(BalloonDeviceError::CreateBalloonDevice)?; + let mmio_dev = + DeviceManager::create_mmio_virtio_device_with_device_change_notification( + Box::new(device), + ctx, + info.config.use_shared_irq.unwrap_or(self.use_shared_irq), + info.config.use_generic_irq.unwrap_or(USE_GENERIC_IRQ), + ) + .map_err(BalloonDeviceError::RegisterBalloonDevice)?; + info.set_device(mmio_dev); + } + + Ok(()) + } + + fn update_balloon_size( + &self, + index: usize, + size_mib: u64, + ) -> std::result::Result<(), BalloonDeviceError> { + let device = self.info_list[index] + .device + .as_ref() + .ok_or_else(|| BalloonDeviceError::NotExist)?; + if let Some(mmio_dev) = device.as_any().downcast_ref::() { + let guard = mmio_dev.state(); + let inner_dev = guard.get_inner_device(); + if let Some(balloon_dev) = inner_dev + .as_any() + .downcast_ref::>() + { + return balloon_dev + .set_size(size_mib) + .map_err(BalloonDeviceError::ResizeFailed); + } + } + Ok(()) + } + + fn get_index_of_balloon_dev(&self, balloon_id: &str) -> Option { + self.info_list + .iter() + .position(|info| info.config.balloon_id.eq(balloon_id)) + } +} + +impl Default for BalloonDeviceMgr { + /// Create a new `BalloonDeviceMgr` object.. + fn default() -> Self { + BalloonDeviceMgr { + info_list: DeviceConfigInfos::new(), + use_shared_irq: USE_SHARED_IRQ, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::test_utils::tests::create_vm_for_test; + + impl Default for BalloonDeviceConfigInfo { + fn default() -> Self { + BalloonDeviceConfigInfo { + balloon_id: "".to_string(), + size_mib: 0, + use_generic_irq: None, + use_shared_irq: None, + f_deflate_on_oom: false, + f_reporting: false, + } + } + } + + #[test] + fn test_balloon_config_check_conflicts() { + let config = BalloonDeviceConfigInfo::default(); + let mut config2 = BalloonDeviceConfigInfo::default(); + assert!(config.check_conflicts(&config2).is_err()); + config2.balloon_id = "dummy_balloon".to_string(); + assert!(config.check_conflicts(&config2).is_ok()); + } + + #[test] + fn test_create_balloon_devices_configs() { + let mgr = BalloonDeviceMgr::default(); + assert_eq!(mgr.info_list.len(), 0); + assert_eq!(mgr.get_index_of_balloon_dev(""), None); + } + + #[test] + fn test_balloon_insert_or_update_device() { + //Init vm for test. + let mut vm = create_vm_for_test(); + + // Test for standard config + let device_op_ctx = DeviceOpContext::new( + Some(vm.epoll_manager().clone()), + vm.device_manager(), + Some(vm.vm_as().unwrap().clone()), + None, + false, + Some(vm.vm_config().clone()), + vm.shared_info().clone(), + ); + + let dummy_balloon_device = BalloonDeviceConfigInfo::default(); + vm.device_manager_mut() + .balloon_manager + .insert_or_update_device(device_op_ctx, dummy_balloon_device) + .unwrap(); + assert_eq!(vm.device_manager().balloon_manager.info_list.len(), 1); + } + + #[test] + fn test_balloon_attach_device() { + //Init vm and insert balloon config for test. + let mut vm = create_vm_for_test(); + let device_op_ctx = DeviceOpContext::new( + Some(vm.epoll_manager().clone()), + vm.device_manager(), + Some(vm.vm_as().unwrap().clone()), + None, + false, + Some(vm.vm_config().clone()), + vm.shared_info().clone(), + ); + + let dummy_balloon_device = BalloonDeviceConfigInfo::default(); + vm.device_manager_mut() + .balloon_manager + .insert_or_update_device(device_op_ctx, dummy_balloon_device) + .unwrap(); + assert_eq!(vm.device_manager().balloon_manager.info_list.len(), 1); + + // Test for standard config + let mut device_op_ctx = DeviceOpContext::new( + Some(vm.epoll_manager().clone()), + vm.device_manager(), + Some(vm.vm_as().unwrap().clone()), + None, + false, + Some(vm.vm_config().clone()), + vm.shared_info().clone(), + ); + assert!(vm + .device_manager_mut() + .balloon_manager + .attach_devices(&mut device_op_ctx) + .is_ok()); + assert_eq!(vm.device_manager().balloon_manager.info_list.len(), 1); + } + + #[test] + fn test_balloon_update_device() { + //Init vm for test. + let mut vm = create_vm_for_test(); + let device_op_ctx = DeviceOpContext::new( + Some(vm.epoll_manager().clone()), + vm.device_manager(), + Some(vm.vm_as().unwrap().clone()), + None, + false, + Some(vm.vm_config().clone()), + vm.shared_info().clone(), + ); + + let dummy_balloon_device = BalloonDeviceConfigInfo::default(); + vm.device_manager_mut() + .balloon_manager + .insert_or_update_device(device_op_ctx, dummy_balloon_device) + .unwrap(); + assert_eq!(vm.device_manager().balloon_manager.info_list.len(), 1); + + let mut device_op_ctx = DeviceOpContext::new( + Some(vm.epoll_manager().clone()), + vm.device_manager(), + Some(vm.vm_as().unwrap().clone()), + None, + false, + Some(vm.vm_config().clone()), + vm.shared_info().clone(), + ); + + assert!(vm + .device_manager_mut() + .balloon_manager + .attach_devices(&mut device_op_ctx) + .is_ok()); + assert_eq!(vm.device_manager().balloon_manager.info_list.len(), 1); + + assert!(vm + .device_manager() + .balloon_manager + .update_balloon_size(0, 200) + .is_ok()); + } +} diff --git a/src/dragonball/src/device_manager/mod.rs b/src/dragonball/src/device_manager/mod.rs index 69112e3ae98e..b007270c94df 100644 --- a/src/dragonball/src/device_manager/mod.rs +++ b/src/dragonball/src/device_manager/mod.rs @@ -96,6 +96,12 @@ pub mod mem_dev_mgr; #[cfg(feature = "virtio-mem")] use self::mem_dev_mgr::MemDeviceMgr; +#[cfg(feature = "virtio-balloon")] +/// Device manager for virtio-balloon devices. +pub mod balloon_dev_mgr; +#[cfg(feature = "virtio-balloon")] +use self::balloon_dev_mgr::BalloonDeviceMgr; + macro_rules! info( ($l:expr, $($args:tt)+) => { slog::info!($l, $($args)+; slog::o!("subsystem" => "device_manager")) @@ -528,6 +534,9 @@ pub struct DeviceManager { #[cfg(feature = "virtio-mem")] pub(crate) mem_manager: MemDeviceMgr, + + #[cfg(feature = "virtio-balloon")] + pub(crate) balloon_manager: BalloonDeviceMgr, } impl DeviceManager { @@ -562,6 +571,8 @@ impl DeviceManager { fs_manager: Arc::new(Mutex::new(FsDeviceMgr::default())), #[cfg(feature = "virtio-mem")] mem_manager: MemDeviceMgr::default(), + #[cfg(feature = "virtio-balloon")] + balloon_manager: BalloonDeviceMgr::default(), } } @@ -1129,6 +1140,8 @@ mod tests { vsock_manager: VsockDeviceMgr::default(), #[cfg(feature = "virtio-mem")] mem_manager: MemDeviceMgr::default(), + #[cfg(feature = "virtio-balloon")] + balloon_manager: BalloonDeviceMgr::default(), #[cfg(target_arch = "aarch64")] mmio_device_info: HashMap::new(), diff --git a/src/dragonball/src/error.rs b/src/dragonball/src/error.rs index 66a24f5629ea..e0aab17fbe0d 100644 --- a/src/dragonball/src/error.rs +++ b/src/dragonball/src/error.rs @@ -188,6 +188,11 @@ pub enum StartMicroVmError { /// Virtio-fs errors. #[error("virtio-fs errors: {0}")] FsDeviceError(#[source] device_manager::fs_dev_mgr::FsDeviceError), + + #[cfg(feature = "virtio-balloon")] + /// Virtio-balloon errors. + #[error("virtio-balloon errors: {0}")] + BalloonDeviceError(#[source] device_manager::balloon_dev_mgr::BalloonDeviceError), } /// Errors associated with starting the instance. diff --git a/src/dragonball/src/vm/mod.rs b/src/dragonball/src/vm/mod.rs index 6e2545c6f1cb..9af046c43b10 100644 --- a/src/dragonball/src/vm/mod.rs +++ b/src/dragonball/src/vm/mod.rs @@ -386,6 +386,19 @@ impl Vm { (dragonball_version, instance_id) } + + pub(crate) fn stop_prealloc(&mut self) -> std::result::Result<(), StartMicroVmError> { + if self.address_space.is_initialized() { + return self + .address_space + .wait_prealloc(true) + .map_err(StartMicroVmError::AddressManagerError); + } + + Err(StartMicroVmError::AddressManagerError( + AddressManagerError::GuestMemoryNotInitialized, + )) + } } impl Vm {