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:
Sebastien Boeuf 2020-04-21 17:28:59 +02:00
parent 24c2b67aa4
commit 49322c5ebe
2 changed files with 119 additions and 3 deletions

View File

@ -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 {}

View File

@ -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 {}