mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2024-10-03 20:15:45 +00:00
virtio-devices: balloon: Factorization and cleanup
Improving the existing code for better readability and in anticipation for adding an additional virtqueue for the free page reporting feature. Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
This commit is contained in:
parent
c325aa3297
commit
414e8e068b
@ -12,41 +12,43 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use super::{
|
use crate::{
|
||||||
ActivateError, ActivateResult, EpollHelper, EpollHelperError, EpollHelperHandler, VirtioCommon,
|
seccomp_filters::Thread, thread_helper::spawn_virtio_thread, ActivateError, ActivateResult,
|
||||||
VirtioDevice, VirtioDeviceType, EPOLL_HELPER_EVENT_LAST, VIRTIO_F_VERSION_1,
|
EpollHelper, EpollHelperError, EpollHelperHandler, GuestMemoryMmap, VirtioCommon, VirtioDevice,
|
||||||
|
VirtioDeviceType, VirtioInterrupt, VirtioInterruptType, EPOLL_HELPER_EVENT_LAST,
|
||||||
|
VIRTIO_F_VERSION_1,
|
||||||
};
|
};
|
||||||
use crate::seccomp_filters::Thread;
|
|
||||||
use crate::thread_helper::spawn_virtio_thread;
|
|
||||||
use crate::GuestMemoryMmap;
|
|
||||||
use crate::{VirtioInterrupt, VirtioInterruptType};
|
|
||||||
use libc::EFD_NONBLOCK;
|
use libc::EFD_NONBLOCK;
|
||||||
use seccompiler::SeccompAction;
|
use seccompiler::SeccompAction;
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::mem::size_of;
|
use std::mem::size_of;
|
||||||
use std::os::unix::io::AsRawFd;
|
use std::os::unix::io::AsRawFd;
|
||||||
use std::result;
|
use std::result;
|
||||||
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
|
use std::sync::{
|
||||||
use std::sync::mpsc;
|
atomic::{AtomicBool, AtomicU64, Ordering},
|
||||||
use std::sync::{Arc, Barrier, Mutex};
|
mpsc, Arc, Barrier, Mutex,
|
||||||
|
};
|
||||||
use versionize::{VersionMap, Versionize, VersionizeResult};
|
use versionize::{VersionMap, Versionize, VersionizeResult};
|
||||||
use versionize_derive::Versionize;
|
use versionize_derive::Versionize;
|
||||||
use virtio_queue::Queue;
|
use virtio_queue::Queue;
|
||||||
use vm_memory::GuestMemory;
|
use vm_memory::{
|
||||||
use vm_memory::{Address, ByteValued, Bytes, GuestAddress, GuestMemoryAtomic, GuestMemoryError};
|
Address, ByteValued, Bytes, GuestAddress, GuestMemory, GuestMemoryAtomic, GuestMemoryError,
|
||||||
use vm_migration::VersionMapped;
|
GuestMemoryRegion,
|
||||||
use vm_migration::{Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable};
|
};
|
||||||
|
use vm_migration::{
|
||||||
|
Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable, VersionMapped,
|
||||||
|
};
|
||||||
use vmm_sys_util::eventfd::EventFd;
|
use vmm_sys_util::eventfd::EventFd;
|
||||||
|
|
||||||
const QUEUE_SIZE: u16 = 128;
|
const QUEUE_SIZE: u16 = 128;
|
||||||
const NUM_QUEUES: usize = 2;
|
const NUM_QUEUES: usize = 2;
|
||||||
const QUEUE_SIZES: &[u16] = &[QUEUE_SIZE; NUM_QUEUES];
|
const QUEUE_SIZES: &[u16] = &[QUEUE_SIZE; NUM_QUEUES];
|
||||||
|
|
||||||
// Get resize event.
|
// Resize event.
|
||||||
const RESIZE_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 1;
|
const RESIZE_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 1;
|
||||||
// New descriptors are pending on the virtio queue.
|
// Inflate virtio queue event.
|
||||||
const INFLATE_QUEUE_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 2;
|
const INFLATE_QUEUE_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 2;
|
||||||
// New descriptors are pending on the virtio queue.
|
// Deflate virtio queue event.
|
||||||
const DEFLATE_QUEUE_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 3;
|
const DEFLATE_QUEUE_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 3;
|
||||||
|
|
||||||
// Size of a PFN in the balloon interface.
|
// Size of a PFN in the balloon interface.
|
||||||
@ -75,8 +77,8 @@ pub enum Error {
|
|||||||
MpscRecvFail(mpsc::RecvError),
|
MpscRecvFail(mpsc::RecvError),
|
||||||
// Resize invalid argument
|
// Resize invalid argument
|
||||||
ResizeInval(String),
|
ResizeInval(String),
|
||||||
// process_queue got wrong ev_type
|
// Invalid queue index
|
||||||
ProcessQueueWrongEvType(u16),
|
InvalidQueueIndex(usize),
|
||||||
// Fail tp signal
|
// Fail tp signal
|
||||||
FailedSignal(io::Error),
|
FailedSignal(io::Error),
|
||||||
/// Descriptor chain is too short
|
/// Descriptor chain is too short
|
||||||
@ -172,23 +174,78 @@ impl BalloonEpollHandler {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_queue(&mut self, ev_type: u16) -> result::Result<(), Error> {
|
fn advise_memory_range(
|
||||||
let queue_index = match ev_type {
|
memory: &GuestMemoryMmap,
|
||||||
INFLATE_QUEUE_EVENT => 0,
|
range_base: GuestAddress,
|
||||||
DEFLATE_QUEUE_EVENT => 1,
|
range_len: usize,
|
||||||
_ => return Err(Error::ProcessQueueWrongEvType(ev_type)),
|
advice: libc::c_int,
|
||||||
|
) -> result::Result<(), Error> {
|
||||||
|
let hva = memory
|
||||||
|
.get_host_address(range_base)
|
||||||
|
.map_err(Error::GuestMemory)?;
|
||||||
|
// Need unsafe to do syscall madvise
|
||||||
|
let res =
|
||||||
|
unsafe { libc::madvise(hva as *mut libc::c_void, range_len as libc::size_t, advice) };
|
||||||
|
if res != 0 {
|
||||||
|
return Err(Error::MadviseFail(io::Error::last_os_error()));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn release_memory_range(
|
||||||
|
memory: &GuestMemoryMmap,
|
||||||
|
range_base: GuestAddress,
|
||||||
|
range_len: usize,
|
||||||
|
) -> result::Result<(), Error> {
|
||||||
|
let region = memory.find_region(range_base).ok_or(Error::GuestMemory(
|
||||||
|
GuestMemoryError::InvalidGuestAddress(range_base),
|
||||||
|
))?;
|
||||||
|
if let Some(f_off) = region.file_offset() {
|
||||||
|
let offset = range_base.0 - region.start_addr().0;
|
||||||
|
let res = unsafe {
|
||||||
|
libc::fallocate64(
|
||||||
|
f_off.file().as_raw_fd(),
|
||||||
|
libc::FALLOC_FL_PUNCH_HOLE | libc::FALLOC_FL_KEEP_SIZE,
|
||||||
|
(offset as u64 + f_off.start()) as libc::off64_t,
|
||||||
|
range_len as libc::off64_t,
|
||||||
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut used_desc_heads = [0; QUEUE_SIZE as usize];
|
if res != 0 {
|
||||||
let mut used_count = 0;
|
return Err(Error::FallocateFail(io::Error::last_os_error()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Self::advise_memory_range(memory, range_base, range_len, libc::MADV_DONTNEED)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn notify_queue(
|
||||||
|
&mut self,
|
||||||
|
queue_index: usize,
|
||||||
|
used_descs: Vec<(u16, u32)>,
|
||||||
|
) -> result::Result<(), Error> {
|
||||||
|
for (desc_index, len) in used_descs.iter() {
|
||||||
|
self.queues[queue_index]
|
||||||
|
.add_used(*desc_index, *len)
|
||||||
|
.map_err(Error::QueueAddUsed)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if !used_descs.is_empty() {
|
||||||
|
self.signal(VirtioInterruptType::Queue(queue_index as u16))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn process_queue(&mut self, queue_index: usize) -> result::Result<(), Error> {
|
||||||
|
let mut used_descs = Vec::new();
|
||||||
for mut desc_chain in self.queues[queue_index]
|
for mut desc_chain in self.queues[queue_index]
|
||||||
.iter()
|
.iter()
|
||||||
.map_err(Error::QueueIterator)?
|
.map_err(Error::QueueIterator)?
|
||||||
{
|
{
|
||||||
let desc = desc_chain.next().ok_or(Error::DescriptorChainTooShort)?;
|
let desc = desc_chain.next().ok_or(Error::DescriptorChainTooShort)?;
|
||||||
|
|
||||||
used_desc_heads[used_count] = desc_chain.head_index();
|
used_descs.push((desc_chain.head_index(), desc.len()));
|
||||||
used_count += 1;
|
|
||||||
|
|
||||||
let data_chunk_size = size_of::<u32>();
|
let data_chunk_size = size_of::<u32>();
|
||||||
|
|
||||||
@ -211,63 +268,27 @@ impl BalloonEpollHandler {
|
|||||||
.map_err(Error::GuestMemory)?;
|
.map_err(Error::GuestMemory)?;
|
||||||
offset += data_chunk_size as u64;
|
offset += data_chunk_size as u64;
|
||||||
|
|
||||||
let gpa = (pfn as u64) << VIRTIO_BALLOON_PFN_SHIFT;
|
let range_base = GuestAddress((pfn as u64) << VIRTIO_BALLOON_PFN_SHIFT);
|
||||||
if let Ok(hva) = desc_chain.memory().get_host_address(GuestAddress(gpa)) {
|
let range_len = 1 << VIRTIO_BALLOON_PFN_SHIFT;
|
||||||
let advice = match ev_type {
|
|
||||||
INFLATE_QUEUE_EVENT => {
|
match queue_index {
|
||||||
let region = desc_chain.memory().find_region(GuestAddress(gpa)).ok_or(
|
0 => {
|
||||||
Error::GuestMemory(GuestMemoryError::InvalidGuestAddress(
|
Self::release_memory_range(desc_chain.memory(), range_base, range_len)?;
|
||||||
GuestAddress(gpa),
|
}
|
||||||
)),
|
1 => {
|
||||||
|
Self::advise_memory_range(
|
||||||
|
desc_chain.memory(),
|
||||||
|
range_base,
|
||||||
|
range_len,
|
||||||
|
libc::MADV_WILLNEED,
|
||||||
)?;
|
)?;
|
||||||
if let Some(f_off) = region.file_offset() {
|
|
||||||
let offset = hva as usize - region.as_ptr() as usize;
|
|
||||||
let res = unsafe {
|
|
||||||
libc::fallocate64(
|
|
||||||
f_off.file().as_raw_fd(),
|
|
||||||
libc::FALLOC_FL_PUNCH_HOLE | libc::FALLOC_FL_KEEP_SIZE,
|
|
||||||
(offset as u64 + f_off.start()) as libc::off64_t,
|
|
||||||
(1 << VIRTIO_BALLOON_PFN_SHIFT) as libc::off64_t,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
if res != 0 {
|
|
||||||
return Err(Error::FallocateFail(io::Error::last_os_error()));
|
|
||||||
}
|
}
|
||||||
}
|
_ => return Err(Error::InvalidQueueIndex(queue_index)),
|
||||||
libc::MADV_DONTNEED
|
|
||||||
}
|
|
||||||
DEFLATE_QUEUE_EVENT => libc::MADV_WILLNEED,
|
|
||||||
_ => return Err(Error::ProcessQueueWrongEvType(ev_type)),
|
|
||||||
};
|
|
||||||
// Need unsafe to do syscall madvise
|
|
||||||
let res = unsafe {
|
|
||||||
libc::madvise(
|
|
||||||
hva as *mut libc::c_void,
|
|
||||||
(1 << VIRTIO_BALLOON_PFN_SHIFT) as libc::size_t,
|
|
||||||
advice,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
if res != 0 {
|
|
||||||
return Err(Error::MadviseFail(io::Error::last_os_error()));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
error!("Address 0x{:x} is not available", gpa);
|
|
||||||
return Err(Error::InvalidRequest);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for &desc_index in &used_desc_heads[..used_count] {
|
self.notify_queue(queue_index, used_descs)
|
||||||
self.queues[queue_index]
|
|
||||||
.add_used(desc_index, 0)
|
|
||||||
.map_err(Error::QueueAddUsed)?;
|
|
||||||
}
|
|
||||||
if used_count > 0 {
|
|
||||||
self.signal(VirtioInterruptType::Queue(queue_index as u16))?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
@ -322,7 +343,7 @@ impl EpollHelperHandler for BalloonEpollHandler {
|
|||||||
if let Err(e) = self.inflate_queue_evt.read() {
|
if let Err(e) = self.inflate_queue_evt.read() {
|
||||||
error!("Failed to get inflate queue event: {:?}", e);
|
error!("Failed to get inflate queue event: {:?}", e);
|
||||||
return true;
|
return true;
|
||||||
} else if let Err(e) = self.process_queue(ev_type) {
|
} else if let Err(e) = self.process_queue(0) {
|
||||||
error!("Failed to signal used inflate queue: {:?}", e);
|
error!("Failed to signal used inflate queue: {:?}", e);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -331,7 +352,7 @@ impl EpollHelperHandler for BalloonEpollHandler {
|
|||||||
if let Err(e) = self.deflate_queue_evt.read() {
|
if let Err(e) = self.deflate_queue_evt.read() {
|
||||||
error!("Failed to get deflate queue event: {:?}", e);
|
error!("Failed to get deflate queue event: {:?}", e);
|
||||||
return true;
|
return true;
|
||||||
} else if let Err(e) = self.process_queue(ev_type) {
|
} else if let Err(e) = self.process_queue(1) {
|
||||||
error!("Failed to signal used deflate queue: {:?}", e);
|
error!("Failed to signal used deflate queue: {:?}", e);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -479,6 +500,9 @@ impl VirtioDevice for Balloon {
|
|||||||
self.common.activate(&queues, &queue_evts, &interrupt_cb)?;
|
self.common.activate(&queues, &queue_evts, &interrupt_cb)?;
|
||||||
let (kill_evt, pause_evt) = self.common.dup_eventfds();
|
let (kill_evt, pause_evt) = self.common.dup_eventfds();
|
||||||
|
|
||||||
|
let inflate_queue_evt = queue_evts.remove(0);
|
||||||
|
let deflate_queue_evt = queue_evts.remove(0);
|
||||||
|
|
||||||
let mut handler = BalloonEpollHandler {
|
let mut handler = BalloonEpollHandler {
|
||||||
config: self.config.clone(),
|
config: self.config.clone(),
|
||||||
resize_receiver: self.resize.get_receiver().map_err(|e| {
|
resize_receiver: self.resize.get_receiver().map_err(|e| {
|
||||||
@ -487,8 +511,8 @@ impl VirtioDevice for Balloon {
|
|||||||
})?,
|
})?,
|
||||||
queues,
|
queues,
|
||||||
interrupt_cb,
|
interrupt_cb,
|
||||||
inflate_queue_evt: queue_evts.remove(0),
|
inflate_queue_evt,
|
||||||
deflate_queue_evt: queue_evts.remove(0),
|
deflate_queue_evt,
|
||||||
kill_evt,
|
kill_evt,
|
||||||
pause_evt,
|
pause_evt,
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user