mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2025-02-01 17:35:19 +00:00
virtio-devices: balloon: Implement free page reporting
Implement the VIRTIO_BALLOON_F_REPORTING feature, indicating to the guest it can report set of free pages. A new virtqueue dedicated for receiving the information about the free pages is created. The VMM releases the memory by punching holes with fallocate() if the guest memory is backed by a file, and madvise() the host about the ranges of memory that shouldn't be needed anymore. Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
This commit is contained in:
parent
052f38fa96
commit
384752647a
@ -41,8 +41,8 @@ use vm_migration::{
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
|
||||
const QUEUE_SIZE: u16 = 128;
|
||||
const NUM_QUEUES: usize = 2;
|
||||
const QUEUE_SIZES: &[u16] = &[QUEUE_SIZE; NUM_QUEUES];
|
||||
const REPORTING_QUEUE_SIZE: u16 = 32;
|
||||
const MIN_NUM_QUEUES: usize = 2;
|
||||
|
||||
// Resize event.
|
||||
const RESIZE_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 1;
|
||||
@ -50,12 +50,17 @@ const RESIZE_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 1;
|
||||
const INFLATE_QUEUE_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 2;
|
||||
// Deflate virtio queue event.
|
||||
const DEFLATE_QUEUE_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 3;
|
||||
// Reporting virtio queue event.
|
||||
const REPORTING_QUEUE_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 4;
|
||||
|
||||
// Size of a PFN in the balloon interface.
|
||||
const VIRTIO_BALLOON_PFN_SHIFT: u64 = 12;
|
||||
|
||||
// Deflate balloon on OOM
|
||||
const VIRTIO_BALLOON_F_DEFLATE_ON_OOM: u64 = 2;
|
||||
// Enable an additional virtqueue to let the guest notify the host about free
|
||||
// pages.
|
||||
const VIRTIO_BALLOON_F_REPORTING: u64 = 5;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
@ -162,6 +167,7 @@ struct BalloonEpollHandler {
|
||||
interrupt_cb: Arc<dyn VirtioInterrupt>,
|
||||
inflate_queue_evt: EventFd,
|
||||
deflate_queue_evt: EventFd,
|
||||
reporting_queue_evt: Option<EventFd>,
|
||||
kill_evt: EventFd,
|
||||
pause_evt: EventFd,
|
||||
}
|
||||
@ -291,6 +297,25 @@ impl BalloonEpollHandler {
|
||||
self.notify_queue(queue_index, used_descs)
|
||||
}
|
||||
|
||||
fn process_reporting_queue(&mut self, queue_index: usize) -> result::Result<(), Error> {
|
||||
let mut used_descs = Vec::new();
|
||||
|
||||
for mut desc_chain in self.queues[queue_index]
|
||||
.iter()
|
||||
.map_err(Error::QueueIterator)?
|
||||
{
|
||||
let mut descs_len = 0;
|
||||
while let Some(desc) = desc_chain.next() {
|
||||
descs_len += desc.len();
|
||||
Self::release_memory_range(desc_chain.memory(), desc.addr(), desc.len() as usize)?;
|
||||
}
|
||||
|
||||
used_descs.push((desc_chain.head_index(), descs_len));
|
||||
}
|
||||
|
||||
self.notify_queue(queue_index, used_descs)
|
||||
}
|
||||
|
||||
fn run(
|
||||
&mut self,
|
||||
paused: Arc<AtomicBool>,
|
||||
@ -300,6 +325,9 @@ impl BalloonEpollHandler {
|
||||
helper.add_event(self.resize_receiver.evt.as_raw_fd(), RESIZE_EVENT)?;
|
||||
helper.add_event(self.inflate_queue_evt.as_raw_fd(), INFLATE_QUEUE_EVENT)?;
|
||||
helper.add_event(self.deflate_queue_evt.as_raw_fd(), DEFLATE_QUEUE_EVENT)?;
|
||||
if let Some(reporting_queue_evt) = self.reporting_queue_evt.as_ref() {
|
||||
helper.add_event(reporting_queue_evt.as_raw_fd(), REPORTING_QUEUE_EVENT)?;
|
||||
}
|
||||
helper.run(paused, paused_sync, self)?;
|
||||
|
||||
Ok(())
|
||||
@ -357,6 +385,20 @@ impl EpollHelperHandler for BalloonEpollHandler {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
REPORTING_QUEUE_EVENT => {
|
||||
if let Some(reporting_queue_evt) = self.reporting_queue_evt.as_ref() {
|
||||
if let Err(e) = reporting_queue_evt.read() {
|
||||
error!("Failed to get reporting queue event: {:?}", e);
|
||||
return true;
|
||||
} else if let Err(e) = self.process_reporting_queue(2) {
|
||||
error!("Failed to signal used inflate queue: {:?}", e);
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
error!("Invalid reporting queue event as no eventfd registered");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
error!("Unknown event for virtio-balloon");
|
||||
return true;
|
||||
@ -392,14 +434,19 @@ impl Balloon {
|
||||
id: String,
|
||||
size: u64,
|
||||
deflate_on_oom: bool,
|
||||
_free_page_reporting: bool,
|
||||
free_page_reporting: bool,
|
||||
seccomp_action: SeccompAction,
|
||||
exit_evt: EventFd,
|
||||
) -> io::Result<Self> {
|
||||
let mut queue_sizes = vec![QUEUE_SIZE; MIN_NUM_QUEUES];
|
||||
let mut avail_features = 1u64 << VIRTIO_F_VERSION_1;
|
||||
if deflate_on_oom {
|
||||
avail_features |= 1u64 << VIRTIO_BALLOON_F_DEFLATE_ON_OOM;
|
||||
}
|
||||
if free_page_reporting {
|
||||
avail_features |= 1u64 << VIRTIO_BALLOON_F_REPORTING;
|
||||
queue_sizes.push(REPORTING_QUEUE_SIZE);
|
||||
}
|
||||
|
||||
let config = VirtioBalloonConfig {
|
||||
num_pages: (size >> VIRTIO_BALLOON_PFN_SHIFT) as u32,
|
||||
@ -411,8 +458,8 @@ impl Balloon {
|
||||
device_type: VirtioDeviceType::Balloon as u32,
|
||||
avail_features,
|
||||
paused_sync: Some(Arc::new(Barrier::new(2))),
|
||||
queue_sizes: QUEUE_SIZES.to_vec(),
|
||||
min_queues: NUM_QUEUES as u16,
|
||||
queue_sizes,
|
||||
min_queues: MIN_NUM_QUEUES as u16,
|
||||
..Default::default()
|
||||
},
|
||||
id,
|
||||
@ -503,6 +550,12 @@ impl VirtioDevice for Balloon {
|
||||
|
||||
let inflate_queue_evt = queue_evts.remove(0);
|
||||
let deflate_queue_evt = queue_evts.remove(0);
|
||||
let reporting_queue_evt =
|
||||
if self.common.feature_acked(VIRTIO_BALLOON_F_REPORTING) && !queue_evts.is_empty() {
|
||||
Some(queue_evts.remove(0))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let mut handler = BalloonEpollHandler {
|
||||
config: self.config.clone(),
|
||||
@ -514,6 +567,7 @@ impl VirtioDevice for Balloon {
|
||||
interrupt_cb,
|
||||
inflate_queue_evt,
|
||||
deflate_queue_evt,
|
||||
reporting_queue_evt,
|
||||
kill_evt,
|
||||
pause_evt,
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user