mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2025-02-21 19:02:30 +00:00
vm-virtio: Implement the Snapshottable trait for Net
Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com> Signed-off-by: Yi Sun <yi.y.sun@linux.intel.com>
This commit is contained in:
parent
24c2b67aa4
commit
49322c5ebe
@ -15,6 +15,7 @@ use super::{
|
||||
ActivateError, ActivateResult, Queue, VirtioDevice, VirtioDeviceType, VirtioInterruptType,
|
||||
};
|
||||
use crate::VirtioInterrupt;
|
||||
use anyhow::anyhow;
|
||||
use epoll;
|
||||
use libc::EAGAIN;
|
||||
use libc::EFD_NONBLOCK;
|
||||
@ -31,7 +32,10 @@ use std::thread;
|
||||
use std::vec::Vec;
|
||||
use virtio_bindings::bindings::virtio_net::*;
|
||||
use vm_memory::{ByteValued, GuestAddressSpace, GuestMemoryAtomic, GuestMemoryMmap};
|
||||
use vm_migration::{Migratable, MigratableError, Pausable, Snapshottable, Transportable};
|
||||
use vm_migration::{
|
||||
Migratable, MigratableError, Pausable, Snapshot, SnapshotDataSection, Snapshottable,
|
||||
Transportable,
|
||||
};
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -234,6 +238,19 @@ impl NetEpollHandler {
|
||||
)
|
||||
.map_err(DeviceError::EpollCtl)?;
|
||||
|
||||
// If there are some already available descriptors on the RX queue,
|
||||
// then we can start the thread while listening onto the TAP.
|
||||
if queues[0].available_descriptors(&self.mem.memory()).unwrap() {
|
||||
epoll::ctl(
|
||||
self.epoll_fd,
|
||||
epoll::ControlOptions::EPOLL_CTL_ADD,
|
||||
self.tap.as_raw_fd(),
|
||||
epoll::Event::new(epoll::Events::EPOLLIN, u64::from(RX_TAP_EVENT)),
|
||||
)
|
||||
.map_err(DeviceError::EpollCtl)?;
|
||||
self.rx_tap_listening = true;
|
||||
}
|
||||
|
||||
let mut events = vec![epoll::Event::new(epoll::Events::empty(), 0); NET_EVENTS_COUNT];
|
||||
|
||||
'epoll: loop {
|
||||
@ -275,6 +292,7 @@ impl NetEpollHandler {
|
||||
// Drain pause event
|
||||
let _ = self.pause_evt.read();
|
||||
debug!("PAUSE_EVENT received, pausing virtio-net epoll loop");
|
||||
|
||||
// We loop here to handle spurious park() returns.
|
||||
// Until we have not resumed, the paused boolean will
|
||||
// be true.
|
||||
@ -307,6 +325,14 @@ pub struct Net {
|
||||
queue_size: Vec<u16>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct NetState {
|
||||
pub avail_features: u64,
|
||||
pub acked_features: u64,
|
||||
pub config: VirtioNetConfig,
|
||||
pub queue_size: Vec<u16>,
|
||||
}
|
||||
|
||||
impl Net {
|
||||
/// Create a new virtio network device with the given TAP interface.
|
||||
pub fn new_with_tap(
|
||||
@ -369,6 +395,24 @@ impl Net {
|
||||
|
||||
Self::new_with_tap(taps, guest_mac, iommu, num_queues, queue_size)
|
||||
}
|
||||
|
||||
fn state(&self) -> NetState {
|
||||
NetState {
|
||||
avail_features: self.avail_features,
|
||||
acked_features: self.acked_features,
|
||||
config: self.config,
|
||||
queue_size: self.queue_size.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
fn set_state(&mut self, state: &NetState) -> Result<()> {
|
||||
self.avail_features = state.avail_features;
|
||||
self.acked_features = state.acked_features;
|
||||
self.config = state.config;
|
||||
self.queue_size = state.queue_size.clone();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Net {
|
||||
@ -567,6 +611,49 @@ impl VirtioDevice for Net {
|
||||
}
|
||||
|
||||
virtio_ctrl_q_pausable!(Net);
|
||||
impl Snapshottable for Net {}
|
||||
const NET_SNAPSHOT_ID: &str = "virtio-net";
|
||||
impl Snapshottable for Net {
|
||||
fn id(&self) -> String {
|
||||
NET_SNAPSHOT_ID.to_string()
|
||||
}
|
||||
|
||||
fn snapshot(&self) -> std::result::Result<Snapshot, MigratableError> {
|
||||
let snapshot =
|
||||
serde_json::to_vec(&self.state()).map_err(|e| MigratableError::Snapshot(e.into()))?;
|
||||
|
||||
let mut net_snapshot = Snapshot::new(NET_SNAPSHOT_ID);
|
||||
net_snapshot.add_data_section(SnapshotDataSection {
|
||||
id: format!("{}-section", NET_SNAPSHOT_ID),
|
||||
snapshot,
|
||||
});
|
||||
|
||||
Ok(net_snapshot)
|
||||
}
|
||||
|
||||
fn restore(&mut self, snapshot: Snapshot) -> std::result::Result<(), MigratableError> {
|
||||
if let Some(net_section) = snapshot
|
||||
.snapshot_data
|
||||
.get(&format!("{}-section", NET_SNAPSHOT_ID))
|
||||
{
|
||||
let net_state = match serde_json::from_slice(&net_section.snapshot) {
|
||||
Ok(state) => state,
|
||||
Err(error) => {
|
||||
return Err(MigratableError::Restore(anyhow!(
|
||||
"Could not deserialize NET {}",
|
||||
error
|
||||
)))
|
||||
}
|
||||
};
|
||||
|
||||
return self.set_state(&net_state).map_err(|e| {
|
||||
MigratableError::Restore(anyhow!("Could not restore NET state {:?}", e))
|
||||
});
|
||||
}
|
||||
|
||||
Err(MigratableError::Restore(anyhow!(
|
||||
"Could not find NET snapshot section"
|
||||
)))
|
||||
}
|
||||
}
|
||||
impl Transportable for Net {}
|
||||
impl Migratable for Net {}
|
||||
|
@ -5,6 +5,7 @@
|
||||
use super::Error as DeviceError;
|
||||
use super::{DescriptorChain, DeviceEventT, Queue};
|
||||
use net_util::{MacAddr, Tap, TapError};
|
||||
use serde::ser::{Serialize, SerializeStruct, Serializer};
|
||||
use std::cmp;
|
||||
use std::fs;
|
||||
use std::io::{self, Write};
|
||||
@ -47,7 +48,7 @@ const CTRL_QUEUE_EVENT: DeviceEventT = 0;
|
||||
const CTRL_EVENT_COUNT: usize = 3;
|
||||
|
||||
#[repr(C, packed)]
|
||||
#[derive(Copy, Clone, Debug, Default)]
|
||||
#[derive(Copy, Clone, Debug, Default, Deserialize)]
|
||||
pub struct VirtioNetConfig {
|
||||
pub mac: [u8; 6],
|
||||
pub status: u16,
|
||||
@ -57,6 +58,34 @@ pub struct VirtioNetConfig {
|
||||
pub duplex: u8,
|
||||
}
|
||||
|
||||
// We must explicitly implement Serialize since the structure is packed and
|
||||
// it's unsafe to borrow from a packed structure. And by default, if we derive
|
||||
// Serialize from serde, it will borrow the values from the structure.
|
||||
// That's why this implementation copies each field separately before it
|
||||
// serializes the entire structure field by field.
|
||||
impl Serialize for VirtioNetConfig {
|
||||
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let mac = self.mac;
|
||||
let status = self.status;
|
||||
let max_virtqueue_pairs = self.max_virtqueue_pairs;
|
||||
let mtu = self.mtu;
|
||||
let speed = self.speed;
|
||||
let duplex = self.duplex;
|
||||
|
||||
let mut virtio_net_config = serializer.serialize_struct("VirtioNetConfig", 17)?;
|
||||
virtio_net_config.serialize_field("mac", &mac)?;
|
||||
virtio_net_config.serialize_field("status", &status)?;
|
||||
virtio_net_config.serialize_field("max_virtqueue_pairs", &max_virtqueue_pairs)?;
|
||||
virtio_net_config.serialize_field("mtu", &mtu)?;
|
||||
virtio_net_config.serialize_field("speed", &speed)?;
|
||||
virtio_net_config.serialize_field("duplex", &duplex)?;
|
||||
virtio_net_config.end()
|
||||
}
|
||||
}
|
||||
|
||||
// Safe because it only has data and has no implicit padding.
|
||||
unsafe impl ByteValued for VirtioNetConfig {}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user