From c75f8b2f896134cbd3376a3eb12098310f0ec16e Mon Sep 17 00:00:00 2001 From: Hui Zhu Date: Tue, 25 Aug 2020 14:44:21 +0800 Subject: [PATCH] virtio-balloon: Add memory_actual_size to vm.info to show memory actual size The virtio-balloon change the memory size is asynchronous. VirtioBalloonConfig.actual of balloon device show current balloon size. This commit add memory_actual_size to vm.info to show memory actual size. Signed-off-by: Hui Zhu --- virtio-devices/src/balloon.rs | 34 ++++++++++++++++++----- virtio-devices/src/device.rs | 22 +++++++++++++++ vmm/src/api/mod.rs | 1 + vmm/src/api/openapi/cloud-hypervisor.yaml | 3 ++ vmm/src/config.rs | 18 ++++++++++++ vmm/src/lib.rs | 10 ++++++- vmm/src/memory_manager.rs | 8 ++++++ vmm/src/vm.rs | 5 ++++ 8 files changed, 93 insertions(+), 8 deletions(-) diff --git a/virtio-devices/src/balloon.rs b/virtio-devices/src/balloon.rs index dedd0ba8b..66232ef3f 100644 --- a/virtio-devices/src/balloon.rs +++ b/virtio-devices/src/balloon.rs @@ -47,9 +47,6 @@ const INFLATE_QUEUE_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 2; // New descriptors are pending on the virtio queue. const DEFLATE_QUEUE_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 3; -// Page shift in the host. -const PAGE_SHIFT: u32 = 12; - // Size of a PFN in the balloon interface. const VIRTIO_BALLOON_PFN_SHIFT: u64 = 12; @@ -78,7 +75,7 @@ pub enum Error { } // Got from include/uapi/linux/virtio_balloon.h -#[repr(C, packed)] +#[repr(C)] #[derive(Copy, Clone, Debug, Default)] struct VirtioBalloonConfig { // Number of pages host wants Guest to give up. @@ -87,6 +84,9 @@ struct VirtioBalloonConfig { actual: u32, } +const CONFIG_ACTUAL_OFFSET: u64 = 4; +const CONFIG_ACTUAL_SIZE: usize = 4; + // Safe because it only has data and has no implicit padding. unsafe impl ByteValued for VirtioBalloonConfig {} @@ -207,7 +207,7 @@ impl BalloonEpollHandler { let res = unsafe { libc::madvise( hva as *mut libc::c_void, - (1 << PAGE_SHIFT) as libc::size_t, + (1 << VIRTIO_BALLOON_PFN_SHIFT) as libc::size_t, advice, ) }; @@ -258,7 +258,8 @@ impl EpollHelperHandler for BalloonEpollHandler { let mut signal_error = false; let r = { let mut config = self.config.lock().unwrap(); - config.num_pages = (self.resize_receiver.get_size() >> PAGE_SHIFT) as u32; + config.num_pages = + (self.resize_receiver.get_size() >> VIRTIO_BALLOON_PFN_SHIFT) as u32; if let Err(e) = self.signal(&VirtioInterruptType::Config, None) { signal_error = true; Err(e) @@ -321,7 +322,7 @@ impl Balloon { let avail_features = 1u64 << VIRTIO_F_VERSION_1; let mut config = VirtioBalloonConfig::default(); - config.num_pages = (size >> PAGE_SHIFT) as u32; + config.num_pages = (size >> VIRTIO_BALLOON_PFN_SHIFT) as u32; Ok(Balloon { common: VirtioCommon { @@ -341,6 +342,11 @@ impl Balloon { pub fn resize(&self, size: u64) -> Result<(), Error> { self.resize.work(size) } + + // Get the actual size of the virtio-balloon. + pub fn get_actual(&self) -> u64 { + (self.config.lock().unwrap().actual as u64) << VIRTIO_BALLOON_PFN_SHIFT + } } impl Drop for Balloon { @@ -373,6 +379,20 @@ impl VirtioDevice for Balloon { self.read_config_from_slice(self.config.lock().unwrap().as_slice(), offset, data); } + fn write_config(&mut self, offset: u64, data: &[u8]) { + // The "actual" field is the only mutable field + if offset != CONFIG_ACTUAL_OFFSET || data.len() != CONFIG_ACTUAL_SIZE { + error!( + "Attempt to write to read-only field: offset {:x} length {}", + offset, + data.len() + ); + return; + } + + self.write_config_helper(self.config.lock().unwrap().as_mut_slice(), offset, data); + } + fn activate( &mut self, mem: GuestMemoryAtomic, diff --git a/virtio-devices/src/device.rs b/virtio-devices/src/device.rs index 1fb5371ec..1a11a0a37 100644 --- a/virtio-devices/src/device.rs +++ b/virtio-devices/src/device.rs @@ -176,6 +176,28 @@ pub trait VirtioDevice: Send { .unwrap(); } } + + /// Helper to allow common implementation of write_config + fn write_config_helper(&self, config: &mut [u8], offset: u64, data: &[u8]) { + let config_len = config.len() as u64; + let data_len = data.len() as u64; + if offset + data_len > config_len { + error!( + "Out-of-bound access to configuration: config_len = {} offset = {:x} length = {} for {}", + config_len, + offset, + data_len, + self.device_type() + ); + return; + } + + if let Some(end) = offset.checked_add(config.len() as u64) { + let mut offset_config = + &mut config[offset as usize..std::cmp::min(end, config_len) as usize]; + offset_config.write_all(data).unwrap(); + } + } } /// Trait providing address translation the same way a physical DMA remapping diff --git a/vmm/src/api/mod.rs b/vmm/src/api/mod.rs index 57dc5d583..8521954c8 100644 --- a/vmm/src/api/mod.rs +++ b/vmm/src/api/mod.rs @@ -145,6 +145,7 @@ pub type ApiResult = std::result::Result; pub struct VmInfo { pub config: Arc>, pub state: VmState, + pub memory_actual_size: u64, } #[derive(Clone, Deserialize, Serialize)] diff --git a/vmm/src/api/openapi/cloud-hypervisor.yaml b/vmm/src/api/openapi/cloud-hypervisor.yaml index 240e02e27..f7cbc1165 100644 --- a/vmm/src/api/openapi/cloud-hypervisor.yaml +++ b/vmm/src/api/openapi/cloud-hypervisor.yaml @@ -360,6 +360,9 @@ components: state: type: string enum: [Created, Running, Shutdown, Paused] + memory_actual_size: + type: integer + format: int64 description: Virtual Machine information VmCounters: diff --git a/vmm/src/config.rs b/vmm/src/config.rs index 3865ec7c0..655ff753d 100644 --- a/vmm/src/config.rs +++ b/vmm/src/config.rs @@ -534,6 +534,24 @@ impl MemoryConfig { zones, }) } + + pub fn total_size(&self) -> u64 { + let mut size = self.size; + if let Some(hotplugged_size) = self.hotplugged_size { + size += hotplugged_size; + } + + if let Some(zones) = &self.zones { + for zone in zones.iter() { + size += zone.size; + if let Some(hotplugged_size) = zone.hotplugged_size { + size += hotplugged_size; + } + } + } + + size + } } impl Default for MemoryConfig { diff --git a/vmm/src/lib.rs b/vmm/src/lib.rs index e8e4066de..4ad9b8f45 100644 --- a/vmm/src/lib.rs +++ b/vmm/src/lib.rs @@ -465,9 +465,17 @@ impl Vmm { None => VmState::Created, }; + let config = Arc::clone(config); + + let mut memory_actual_size = config.lock().unwrap().memory.total_size(); + if let Some(vm) = &self.vm { + memory_actual_size -= vm.get_balloon_actual(); + } + Ok(VmInfo { - config: Arc::clone(config), + config, state, + memory_actual_size, }) } None => Err(VmError::VmNotCreated), diff --git a/vmm/src/memory_manager.rs b/vmm/src/memory_manager.rs index c89fcea2c..f9a909074 100644 --- a/vmm/src/memory_manager.rs +++ b/vmm/src/memory_manager.rs @@ -1236,6 +1236,14 @@ impl MemoryManager { Ok(balloon_size) } + pub fn get_balloon_actual(&self) -> u64 { + if let Some(balloon) = &self.balloon { + return balloon.lock().unwrap().get_actual(); + } + + 0 + } + /// In case this function resulted in adding a new memory region to the /// guest memory, the new region is returned to the caller. The virtio-mem /// use case never adds a new region as the whole hotpluggable memory has diff --git a/vmm/src/vm.rs b/vmm/src/vm.rs index 151755551..1a6464d88 100644 --- a/vmm/src/vm.rs +++ b/vmm/src/vm.rs @@ -1493,6 +1493,11 @@ impl Vm { Ok(()) } + + /// Gets the actual size of the balloon. + pub fn get_balloon_actual(&self) -> u64 { + self.memory_manager.lock().unwrap().get_balloon_actual() + } } impl Pausable for Vm {