mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2026-08-05 02:19:16 +00:00
vmm: virtio-devices: Restore every VirtioDevice upon creation
Following the new design proposal to improve the restore codepath when migrating a VM, all virtio devices are supplied with an optional state they can use to restore from. The restore() implementation every device was providing has been removed in order to prevent from going through the restoration twice. Here is the list of devices now following the new restore design: - Block (virtio-block) - Net (virtio-net) - Rng (virtio-rng) - Fs (vhost-user-fs) - Blk (vhost-user-block) - Net (vhost-user-net) - Pmem (virtio-pmem) - Vsock (virtio-vsock) - Mem (virtio-mem) - Balloon (virtio-balloon) - Watchdog (virtio-watchdog) - Vdpa (vDPA) - Console (virtio-console) - Iommu (virtio-iommu) Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
This commit is contained in:
@@ -69,112 +69,113 @@ impl Blk {
|
||||
pub fn new(
|
||||
id: String,
|
||||
vu_cfg: VhostUserConfig,
|
||||
restoring: bool,
|
||||
seccomp_action: SeccompAction,
|
||||
exit_evt: EventFd,
|
||||
iommu: bool,
|
||||
state: Option<State>,
|
||||
) -> Result<Blk> {
|
||||
let num_queues = vu_cfg.num_queues;
|
||||
|
||||
if restoring {
|
||||
// We need 'queue_sizes' to report a number of queues that will be
|
||||
// enough to handle all the potential queues. VirtioPciDevice::new()
|
||||
// will create the actual queues based on this information.
|
||||
return Ok(Blk {
|
||||
common: VirtioCommon {
|
||||
device_type: VirtioDeviceType::Block as u32,
|
||||
queue_sizes: vec![vu_cfg.queue_size; num_queues],
|
||||
paused_sync: Some(Arc::new(Barrier::new(2))),
|
||||
min_queues: DEFAULT_QUEUE_NUMBER as u16,
|
||||
..Default::default()
|
||||
},
|
||||
vu_common: VhostUserCommon {
|
||||
socket_path: vu_cfg.socket,
|
||||
vu_num_queues: num_queues,
|
||||
..Default::default()
|
||||
},
|
||||
id,
|
||||
config: VirtioBlockConfig::default(),
|
||||
guest_memory: None,
|
||||
epoll_thread: None,
|
||||
seccomp_action,
|
||||
exit_evt,
|
||||
iommu,
|
||||
});
|
||||
}
|
||||
|
||||
let mut vu =
|
||||
VhostUserHandle::connect_vhost_user(false, &vu_cfg.socket, num_queues as u64, false)?;
|
||||
|
||||
// Filling device and vring features VMM supports.
|
||||
let mut avail_features = 1 << VIRTIO_BLK_F_SIZE_MAX
|
||||
| 1 << VIRTIO_BLK_F_SEG_MAX
|
||||
| 1 << VIRTIO_BLK_F_GEOMETRY
|
||||
| 1 << VIRTIO_BLK_F_RO
|
||||
| 1 << VIRTIO_BLK_F_BLK_SIZE
|
||||
| 1 << VIRTIO_BLK_F_FLUSH
|
||||
| 1 << VIRTIO_BLK_F_TOPOLOGY
|
||||
| 1 << VIRTIO_BLK_F_CONFIG_WCE
|
||||
| 1 << VIRTIO_BLK_F_DISCARD
|
||||
| 1 << VIRTIO_BLK_F_WRITE_ZEROES
|
||||
| DEFAULT_VIRTIO_FEATURES;
|
||||
let (avail_features, acked_features, acked_protocol_features, vu_num_queues, config) =
|
||||
if let Some(state) = state {
|
||||
info!("Restoring vhost-user-block {}", id);
|
||||
|
||||
if num_queues > 1 {
|
||||
avail_features |= 1 << VIRTIO_BLK_F_MQ;
|
||||
}
|
||||
vu.set_protocol_features_vhost_user(
|
||||
state.acked_features,
|
||||
state.acked_protocol_features,
|
||||
)?;
|
||||
|
||||
let avail_protocol_features = VhostUserProtocolFeatures::CONFIG
|
||||
| VhostUserProtocolFeatures::MQ
|
||||
| VhostUserProtocolFeatures::CONFIGURE_MEM_SLOTS
|
||||
| VhostUserProtocolFeatures::REPLY_ACK
|
||||
| VhostUserProtocolFeatures::INFLIGHT_SHMFD
|
||||
| VhostUserProtocolFeatures::LOG_SHMFD;
|
||||
|
||||
let (acked_features, acked_protocol_features) =
|
||||
vu.negotiate_features_vhost_user(avail_features, avail_protocol_features)?;
|
||||
|
||||
let backend_num_queues =
|
||||
if acked_protocol_features & VhostUserProtocolFeatures::MQ.bits() != 0 {
|
||||
vu.socket_handle()
|
||||
.get_queue_num()
|
||||
.map_err(Error::VhostUserGetQueueMaxNum)? as usize
|
||||
(
|
||||
state.avail_features,
|
||||
state.acked_features,
|
||||
state.acked_protocol_features,
|
||||
state.vu_num_queues,
|
||||
state.config,
|
||||
)
|
||||
} else {
|
||||
DEFAULT_QUEUE_NUMBER
|
||||
};
|
||||
// Filling device and vring features VMM supports.
|
||||
let mut avail_features = 1 << VIRTIO_BLK_F_SIZE_MAX
|
||||
| 1 << VIRTIO_BLK_F_SEG_MAX
|
||||
| 1 << VIRTIO_BLK_F_GEOMETRY
|
||||
| 1 << VIRTIO_BLK_F_RO
|
||||
| 1 << VIRTIO_BLK_F_BLK_SIZE
|
||||
| 1 << VIRTIO_BLK_F_FLUSH
|
||||
| 1 << VIRTIO_BLK_F_TOPOLOGY
|
||||
| 1 << VIRTIO_BLK_F_CONFIG_WCE
|
||||
| 1 << VIRTIO_BLK_F_DISCARD
|
||||
| 1 << VIRTIO_BLK_F_WRITE_ZEROES
|
||||
| DEFAULT_VIRTIO_FEATURES;
|
||||
|
||||
if num_queues > backend_num_queues {
|
||||
error!("vhost-user-blk requested too many queues ({}) since the backend only supports {}\n",
|
||||
if num_queues > 1 {
|
||||
avail_features |= 1 << VIRTIO_BLK_F_MQ;
|
||||
}
|
||||
|
||||
let avail_protocol_features = VhostUserProtocolFeatures::CONFIG
|
||||
| VhostUserProtocolFeatures::MQ
|
||||
| VhostUserProtocolFeatures::CONFIGURE_MEM_SLOTS
|
||||
| VhostUserProtocolFeatures::REPLY_ACK
|
||||
| VhostUserProtocolFeatures::INFLIGHT_SHMFD
|
||||
| VhostUserProtocolFeatures::LOG_SHMFD;
|
||||
|
||||
let (acked_features, acked_protocol_features) =
|
||||
vu.negotiate_features_vhost_user(avail_features, avail_protocol_features)?;
|
||||
|
||||
let backend_num_queues =
|
||||
if acked_protocol_features & VhostUserProtocolFeatures::MQ.bits() != 0 {
|
||||
vu.socket_handle()
|
||||
.get_queue_num()
|
||||
.map_err(Error::VhostUserGetQueueMaxNum)?
|
||||
as usize
|
||||
} else {
|
||||
DEFAULT_QUEUE_NUMBER
|
||||
};
|
||||
|
||||
if num_queues > backend_num_queues {
|
||||
error!("vhost-user-blk requested too many queues ({}) since the backend only supports {}\n",
|
||||
num_queues, backend_num_queues);
|
||||
return Err(Error::BadQueueNum);
|
||||
}
|
||||
return Err(Error::BadQueueNum);
|
||||
}
|
||||
|
||||
let config_len = mem::size_of::<VirtioBlockConfig>();
|
||||
let config_space: Vec<u8> = vec![0u8; config_len as usize];
|
||||
let (_, config_space) = vu
|
||||
.socket_handle()
|
||||
.get_config(
|
||||
VHOST_USER_CONFIG_OFFSET,
|
||||
config_len as u32,
|
||||
VhostUserConfigFlags::WRITABLE,
|
||||
config_space.as_slice(),
|
||||
)
|
||||
.map_err(Error::VhostUserGetConfig)?;
|
||||
let mut config = VirtioBlockConfig::default();
|
||||
if let Some(backend_config) = VirtioBlockConfig::from_slice(config_space.as_slice()) {
|
||||
config = *backend_config;
|
||||
config.num_queues = num_queues as u16;
|
||||
}
|
||||
let config_len = mem::size_of::<VirtioBlockConfig>();
|
||||
let config_space: Vec<u8> = vec![0u8; config_len as usize];
|
||||
let (_, config_space) = vu
|
||||
.socket_handle()
|
||||
.get_config(
|
||||
VHOST_USER_CONFIG_OFFSET,
|
||||
config_len as u32,
|
||||
VhostUserConfigFlags::WRITABLE,
|
||||
config_space.as_slice(),
|
||||
)
|
||||
.map_err(Error::VhostUserGetConfig)?;
|
||||
let mut config = VirtioBlockConfig::default();
|
||||
if let Some(backend_config) = VirtioBlockConfig::from_slice(config_space.as_slice())
|
||||
{
|
||||
config = *backend_config;
|
||||
config.num_queues = num_queues as u16;
|
||||
}
|
||||
|
||||
(
|
||||
acked_features,
|
||||
// If part of the available features that have been acked,
|
||||
// the PROTOCOL_FEATURES bit must be already set through
|
||||
// the VIRTIO acked features as we know the guest would
|
||||
// never ack it, thus the feature would be lost.
|
||||
acked_features & VhostUserVirtioFeatures::PROTOCOL_FEATURES.bits(),
|
||||
acked_protocol_features,
|
||||
num_queues,
|
||||
config,
|
||||
)
|
||||
};
|
||||
|
||||
Ok(Blk {
|
||||
common: VirtioCommon {
|
||||
device_type: VirtioDeviceType::Block as u32,
|
||||
queue_sizes: vec![vu_cfg.queue_size; num_queues],
|
||||
avail_features: acked_features,
|
||||
// If part of the available features that have been acked, the
|
||||
// PROTOCOL_FEATURES bit must be already set through the VIRTIO
|
||||
// acked features as we know the guest would never ack it, thus
|
||||
// the feature would be lost.
|
||||
acked_features: acked_features & VhostUserVirtioFeatures::PROTOCOL_FEATURES.bits(),
|
||||
avail_features,
|
||||
acked_features,
|
||||
paused_sync: Some(Arc::new(Barrier::new(2))),
|
||||
min_queues: DEFAULT_QUEUE_NUMBER as u16,
|
||||
..Default::default()
|
||||
@@ -183,7 +184,7 @@ impl Blk {
|
||||
vu: Some(Arc::new(Mutex::new(vu))),
|
||||
acked_protocol_features,
|
||||
socket_path: vu_cfg.socket,
|
||||
vu_num_queues: num_queues,
|
||||
vu_num_queues,
|
||||
..Default::default()
|
||||
},
|
||||
id,
|
||||
@@ -205,24 +206,6 @@ impl Blk {
|
||||
vu_num_queues: self.vu_common.vu_num_queues,
|
||||
}
|
||||
}
|
||||
|
||||
fn set_state(&mut self, state: &State) {
|
||||
self.common.avail_features = state.avail_features;
|
||||
self.common.acked_features = state.acked_features;
|
||||
self.config = state.config;
|
||||
self.vu_common.acked_protocol_features = state.acked_protocol_features;
|
||||
self.vu_common.vu_num_queues = state.vu_num_queues;
|
||||
|
||||
if let Err(e) = self
|
||||
.vu_common
|
||||
.restore_backend_connection(self.common.acked_features)
|
||||
{
|
||||
error!(
|
||||
"Failed restoring connection with vhost-user backend: {:?}",
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Blk {
|
||||
@@ -392,11 +375,6 @@ impl Snapshottable for Blk {
|
||||
fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> {
|
||||
self.vu_common.snapshot(&self.id(), &self.state())
|
||||
}
|
||||
|
||||
fn restore(&mut self, snapshot: Snapshot) -> std::result::Result<(), MigratableError> {
|
||||
self.set_state(&snapshot.to_versioned_state(&self.id)?);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
impl Transportable for Blk {}
|
||||
|
||||
|
||||
@@ -315,102 +315,107 @@ impl Fs {
|
||||
queue_size: u16,
|
||||
cache: Option<(VirtioSharedMemoryList, MmapRegion)>,
|
||||
seccomp_action: SeccompAction,
|
||||
restoring: bool,
|
||||
exit_evt: EventFd,
|
||||
iommu: bool,
|
||||
state: Option<State>,
|
||||
) -> Result<Fs> {
|
||||
let mut slave_req_support = false;
|
||||
|
||||
// Calculate the actual number of queues needed.
|
||||
let num_queues = NUM_QUEUE_OFFSET + req_num_queues;
|
||||
|
||||
if restoring {
|
||||
// We need 'queue_sizes' to report a number of queues that will be
|
||||
// enough to handle all the potential queues. VirtioPciDevice::new()
|
||||
// will create the actual queues based on this information.
|
||||
return Ok(Fs {
|
||||
common: VirtioCommon {
|
||||
device_type: VirtioDeviceType::Fs as u32,
|
||||
queue_sizes: vec![queue_size; num_queues],
|
||||
paused_sync: Some(Arc::new(Barrier::new(2))),
|
||||
min_queues: 1,
|
||||
..Default::default()
|
||||
},
|
||||
vu_common: VhostUserCommon {
|
||||
socket_path: path.to_string(),
|
||||
vu_num_queues: num_queues,
|
||||
..Default::default()
|
||||
},
|
||||
id,
|
||||
config: VirtioFsConfig::default(),
|
||||
cache,
|
||||
slave_req_support,
|
||||
seccomp_action,
|
||||
guest_memory: None,
|
||||
epoll_thread: None,
|
||||
exit_evt,
|
||||
iommu,
|
||||
});
|
||||
}
|
||||
|
||||
// Connect to the vhost-user socket.
|
||||
let mut vu = VhostUserHandle::connect_vhost_user(false, path, num_queues as u64, false)?;
|
||||
|
||||
// Filling device and vring features VMM supports.
|
||||
let avail_features = DEFAULT_VIRTIO_FEATURES;
|
||||
let (
|
||||
avail_features,
|
||||
acked_features,
|
||||
acked_protocol_features,
|
||||
vu_num_queues,
|
||||
config,
|
||||
slave_req_support,
|
||||
) = if let Some(state) = state {
|
||||
info!("Restoring vhost-user-fs {}", id);
|
||||
|
||||
let mut avail_protocol_features = VhostUserProtocolFeatures::MQ
|
||||
| VhostUserProtocolFeatures::CONFIGURE_MEM_SLOTS
|
||||
| VhostUserProtocolFeatures::REPLY_ACK
|
||||
| VhostUserProtocolFeatures::INFLIGHT_SHMFD
|
||||
| VhostUserProtocolFeatures::LOG_SHMFD;
|
||||
let slave_protocol_features =
|
||||
VhostUserProtocolFeatures::SLAVE_REQ | VhostUserProtocolFeatures::SLAVE_SEND_FD;
|
||||
if cache.is_some() {
|
||||
avail_protocol_features |= slave_protocol_features;
|
||||
}
|
||||
vu.set_protocol_features_vhost_user(
|
||||
state.acked_features,
|
||||
state.acked_protocol_features,
|
||||
)?;
|
||||
|
||||
let (acked_features, acked_protocol_features) =
|
||||
vu.negotiate_features_vhost_user(avail_features, avail_protocol_features)?;
|
||||
(
|
||||
state.avail_features,
|
||||
state.acked_features,
|
||||
state.acked_protocol_features,
|
||||
state.vu_num_queues,
|
||||
state.config,
|
||||
state.slave_req_support,
|
||||
)
|
||||
} else {
|
||||
// Filling device and vring features VMM supports.
|
||||
let avail_features = DEFAULT_VIRTIO_FEATURES;
|
||||
|
||||
let backend_num_queues =
|
||||
if acked_protocol_features & VhostUserProtocolFeatures::MQ.bits() != 0 {
|
||||
vu.socket_handle()
|
||||
.get_queue_num()
|
||||
.map_err(Error::VhostUserGetQueueMaxNum)? as usize
|
||||
} else {
|
||||
DEFAULT_QUEUE_NUMBER
|
||||
};
|
||||
let mut avail_protocol_features = VhostUserProtocolFeatures::MQ
|
||||
| VhostUserProtocolFeatures::CONFIGURE_MEM_SLOTS
|
||||
| VhostUserProtocolFeatures::REPLY_ACK
|
||||
| VhostUserProtocolFeatures::INFLIGHT_SHMFD
|
||||
| VhostUserProtocolFeatures::LOG_SHMFD;
|
||||
let slave_protocol_features =
|
||||
VhostUserProtocolFeatures::SLAVE_REQ | VhostUserProtocolFeatures::SLAVE_SEND_FD;
|
||||
if cache.is_some() {
|
||||
avail_protocol_features |= slave_protocol_features;
|
||||
}
|
||||
|
||||
if num_queues > backend_num_queues {
|
||||
error!(
|
||||
let (acked_features, acked_protocol_features) =
|
||||
vu.negotiate_features_vhost_user(avail_features, avail_protocol_features)?;
|
||||
|
||||
let backend_num_queues =
|
||||
if acked_protocol_features & VhostUserProtocolFeatures::MQ.bits() != 0 {
|
||||
vu.socket_handle()
|
||||
.get_queue_num()
|
||||
.map_err(Error::VhostUserGetQueueMaxNum)? as usize
|
||||
} else {
|
||||
DEFAULT_QUEUE_NUMBER
|
||||
};
|
||||
|
||||
if num_queues > backend_num_queues {
|
||||
error!(
|
||||
"vhost-user-fs requested too many queues ({}) since the backend only supports {}\n",
|
||||
num_queues, backend_num_queues
|
||||
);
|
||||
return Err(Error::BadQueueNum);
|
||||
}
|
||||
return Err(Error::BadQueueNum);
|
||||
}
|
||||
|
||||
if acked_protocol_features & slave_protocol_features.bits()
|
||||
== slave_protocol_features.bits()
|
||||
{
|
||||
slave_req_support = true;
|
||||
}
|
||||
if acked_protocol_features & slave_protocol_features.bits()
|
||||
== slave_protocol_features.bits()
|
||||
{
|
||||
slave_req_support = true;
|
||||
}
|
||||
|
||||
// Create virtio-fs device configuration.
|
||||
let mut config = VirtioFsConfig::default();
|
||||
let tag_bytes_vec = tag.to_string().into_bytes();
|
||||
config.tag[..tag_bytes_vec.len()].copy_from_slice(tag_bytes_vec.as_slice());
|
||||
config.num_request_queues = req_num_queues as u32;
|
||||
// Create virtio-fs device configuration.
|
||||
let mut config = VirtioFsConfig::default();
|
||||
let tag_bytes_vec = tag.to_string().into_bytes();
|
||||
config.tag[..tag_bytes_vec.len()].copy_from_slice(tag_bytes_vec.as_slice());
|
||||
config.num_request_queues = req_num_queues as u32;
|
||||
|
||||
Ok(Fs {
|
||||
common: VirtioCommon {
|
||||
device_type: VirtioDeviceType::Fs as u32,
|
||||
avail_features: acked_features,
|
||||
(
|
||||
acked_features,
|
||||
// If part of the available features that have been acked, the
|
||||
// PROTOCOL_FEATURES bit must be already set through the VIRTIO
|
||||
// acked features as we know the guest would never ack it, thus
|
||||
// the feature would be lost.
|
||||
acked_features: acked_features & VhostUserVirtioFeatures::PROTOCOL_FEATURES.bits(),
|
||||
acked_features & VhostUserVirtioFeatures::PROTOCOL_FEATURES.bits(),
|
||||
acked_protocol_features,
|
||||
num_queues,
|
||||
config,
|
||||
slave_req_support,
|
||||
)
|
||||
};
|
||||
|
||||
Ok(Fs {
|
||||
common: VirtioCommon {
|
||||
device_type: VirtioDeviceType::Fs as u32,
|
||||
avail_features,
|
||||
acked_features,
|
||||
queue_sizes: vec![queue_size; num_queues],
|
||||
paused_sync: Some(Arc::new(Barrier::new(2))),
|
||||
min_queues: 1,
|
||||
@@ -420,7 +425,7 @@ impl Fs {
|
||||
vu: Some(Arc::new(Mutex::new(vu))),
|
||||
acked_protocol_features,
|
||||
socket_path: path.to_string(),
|
||||
vu_num_queues: num_queues,
|
||||
vu_num_queues,
|
||||
..Default::default()
|
||||
},
|
||||
id,
|
||||
@@ -445,25 +450,6 @@ impl Fs {
|
||||
slave_req_support: self.slave_req_support,
|
||||
}
|
||||
}
|
||||
|
||||
fn set_state(&mut self, state: &State) {
|
||||
self.common.avail_features = state.avail_features;
|
||||
self.common.acked_features = state.acked_features;
|
||||
self.config = state.config;
|
||||
self.vu_common.acked_protocol_features = state.acked_protocol_features;
|
||||
self.vu_common.vu_num_queues = state.vu_num_queues;
|
||||
self.slave_req_support = state.slave_req_support;
|
||||
|
||||
if let Err(e) = self
|
||||
.vu_common
|
||||
.restore_backend_connection(self.common.acked_features)
|
||||
{
|
||||
error!(
|
||||
"Failed restoring connection with vhost-user backend: {:?}",
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Fs {
|
||||
@@ -663,11 +649,6 @@ impl Snapshottable for Fs {
|
||||
fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> {
|
||||
self.vu_common.snapshot(&self.id(), &self.state())
|
||||
}
|
||||
|
||||
fn restore(&mut self, snapshot: Snapshot) -> std::result::Result<(), MigratableError> {
|
||||
self.set_state(&snapshot.to_versioned_state(&self.id)?);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
impl Transportable for Fs {}
|
||||
|
||||
|
||||
@@ -74,115 +74,123 @@ impl Net {
|
||||
vu_cfg: VhostUserConfig,
|
||||
server: bool,
|
||||
seccomp_action: SeccompAction,
|
||||
restoring: bool,
|
||||
exit_evt: EventFd,
|
||||
iommu: bool,
|
||||
state: Option<State>,
|
||||
) -> Result<Net> {
|
||||
let mut num_queues = vu_cfg.num_queues;
|
||||
|
||||
if restoring {
|
||||
// We need 'queue_sizes' to report a number of queues that will be
|
||||
// enough to handle all the potential queues. Including the control
|
||||
// queue (with +1) will guarantee that. VirtioPciDevice::new() will
|
||||
// create the actual queues based on this information.
|
||||
return Ok(Net {
|
||||
common: VirtioCommon {
|
||||
device_type: VirtioDeviceType::Net as u32,
|
||||
queue_sizes: vec![vu_cfg.queue_size; num_queues + 1],
|
||||
paused_sync: Some(Arc::new(Barrier::new(2))),
|
||||
min_queues: DEFAULT_QUEUE_NUMBER as u16,
|
||||
..Default::default()
|
||||
},
|
||||
vu_common: VhostUserCommon {
|
||||
socket_path: vu_cfg.socket,
|
||||
vu_num_queues: num_queues,
|
||||
server,
|
||||
..Default::default()
|
||||
},
|
||||
id,
|
||||
config: VirtioNetConfig::default(),
|
||||
guest_memory: None,
|
||||
ctrl_queue_epoll_thread: None,
|
||||
epoll_thread: None,
|
||||
seccomp_action,
|
||||
exit_evt,
|
||||
iommu,
|
||||
});
|
||||
}
|
||||
|
||||
// Filling device and vring features VMM supports.
|
||||
let mut avail_features = 1 << VIRTIO_NET_F_CSUM
|
||||
| 1 << VIRTIO_NET_F_GUEST_CSUM
|
||||
| 1 << VIRTIO_NET_F_GUEST_TSO4
|
||||
| 1 << VIRTIO_NET_F_GUEST_TSO6
|
||||
| 1 << VIRTIO_NET_F_GUEST_ECN
|
||||
| 1 << VIRTIO_NET_F_GUEST_UFO
|
||||
| 1 << VIRTIO_NET_F_HOST_TSO4
|
||||
| 1 << VIRTIO_NET_F_HOST_TSO6
|
||||
| 1 << VIRTIO_NET_F_HOST_ECN
|
||||
| 1 << VIRTIO_NET_F_HOST_UFO
|
||||
| 1 << VIRTIO_NET_F_MRG_RXBUF
|
||||
| 1 << VIRTIO_NET_F_CTRL_VQ
|
||||
| 1 << VIRTIO_F_RING_EVENT_IDX
|
||||
| 1 << VIRTIO_F_VERSION_1
|
||||
| VhostUserVirtioFeatures::PROTOCOL_FEATURES.bits();
|
||||
|
||||
if mtu.is_some() {
|
||||
avail_features |= 1u64 << VIRTIO_NET_F_MTU;
|
||||
}
|
||||
|
||||
let mut config = VirtioNetConfig::default();
|
||||
build_net_config_space(&mut config, mac_addr, num_queues, mtu, &mut avail_features);
|
||||
|
||||
let mut vu =
|
||||
VhostUserHandle::connect_vhost_user(server, &vu_cfg.socket, num_queues as u64, false)?;
|
||||
|
||||
let avail_protocol_features = VhostUserProtocolFeatures::MQ
|
||||
| VhostUserProtocolFeatures::CONFIGURE_MEM_SLOTS
|
||||
| VhostUserProtocolFeatures::REPLY_ACK
|
||||
| VhostUserProtocolFeatures::INFLIGHT_SHMFD
|
||||
| VhostUserProtocolFeatures::LOG_SHMFD;
|
||||
let (avail_features, acked_features, acked_protocol_features, vu_num_queues, config) =
|
||||
if let Some(state) = state {
|
||||
info!("Restoring vhost-user-net {}", id);
|
||||
|
||||
let (mut acked_features, acked_protocol_features) =
|
||||
vu.negotiate_features_vhost_user(avail_features, avail_protocol_features)?;
|
||||
// The backend acknowledged features must not contain
|
||||
// VIRTIO_NET_F_MAC since we don't expect the backend
|
||||
// to handle it.
|
||||
let backend_acked_features = state.acked_features & !(1 << VIRTIO_NET_F_MAC);
|
||||
|
||||
let backend_num_queues =
|
||||
if acked_protocol_features & VhostUserProtocolFeatures::MQ.bits() != 0 {
|
||||
vu.socket_handle()
|
||||
.get_queue_num()
|
||||
.map_err(Error::VhostUserGetQueueMaxNum)? as usize
|
||||
vu.set_protocol_features_vhost_user(
|
||||
backend_acked_features,
|
||||
state.acked_protocol_features,
|
||||
)?;
|
||||
|
||||
// If the control queue feature has been negotiated, let's
|
||||
// increase the number of queues.
|
||||
if state.acked_features & (1 << VIRTIO_NET_F_CTRL_VQ) != 0 {
|
||||
num_queues += 1;
|
||||
}
|
||||
|
||||
(
|
||||
state.avail_features,
|
||||
state.acked_features,
|
||||
state.acked_protocol_features,
|
||||
state.vu_num_queues,
|
||||
state.config,
|
||||
)
|
||||
} else {
|
||||
DEFAULT_QUEUE_NUMBER
|
||||
};
|
||||
// Filling device and vring features VMM supports.
|
||||
let mut avail_features = 1 << VIRTIO_NET_F_CSUM
|
||||
| 1 << VIRTIO_NET_F_GUEST_CSUM
|
||||
| 1 << VIRTIO_NET_F_GUEST_TSO4
|
||||
| 1 << VIRTIO_NET_F_GUEST_TSO6
|
||||
| 1 << VIRTIO_NET_F_GUEST_ECN
|
||||
| 1 << VIRTIO_NET_F_GUEST_UFO
|
||||
| 1 << VIRTIO_NET_F_HOST_TSO4
|
||||
| 1 << VIRTIO_NET_F_HOST_TSO6
|
||||
| 1 << VIRTIO_NET_F_HOST_ECN
|
||||
| 1 << VIRTIO_NET_F_HOST_UFO
|
||||
| 1 << VIRTIO_NET_F_MRG_RXBUF
|
||||
| 1 << VIRTIO_NET_F_CTRL_VQ
|
||||
| 1 << VIRTIO_F_RING_EVENT_IDX
|
||||
| 1 << VIRTIO_F_VERSION_1
|
||||
| VhostUserVirtioFeatures::PROTOCOL_FEATURES.bits();
|
||||
|
||||
if num_queues > backend_num_queues {
|
||||
error!("vhost-user-net requested too many queues ({}) since the backend only supports {}\n",
|
||||
if mtu.is_some() {
|
||||
avail_features |= 1u64 << VIRTIO_NET_F_MTU;
|
||||
}
|
||||
|
||||
let mut config = VirtioNetConfig::default();
|
||||
build_net_config_space(&mut config, mac_addr, num_queues, mtu, &mut avail_features);
|
||||
|
||||
let avail_protocol_features = VhostUserProtocolFeatures::MQ
|
||||
| VhostUserProtocolFeatures::CONFIGURE_MEM_SLOTS
|
||||
| VhostUserProtocolFeatures::REPLY_ACK
|
||||
| VhostUserProtocolFeatures::INFLIGHT_SHMFD
|
||||
| VhostUserProtocolFeatures::LOG_SHMFD;
|
||||
|
||||
let (mut acked_features, acked_protocol_features) =
|
||||
vu.negotiate_features_vhost_user(avail_features, avail_protocol_features)?;
|
||||
|
||||
let backend_num_queues =
|
||||
if acked_protocol_features & VhostUserProtocolFeatures::MQ.bits() != 0 {
|
||||
vu.socket_handle()
|
||||
.get_queue_num()
|
||||
.map_err(Error::VhostUserGetQueueMaxNum)?
|
||||
as usize
|
||||
} else {
|
||||
DEFAULT_QUEUE_NUMBER
|
||||
};
|
||||
|
||||
if num_queues > backend_num_queues {
|
||||
error!("vhost-user-net requested too many queues ({}) since the backend only supports {}\n",
|
||||
num_queues, backend_num_queues);
|
||||
return Err(Error::BadQueueNum);
|
||||
}
|
||||
return Err(Error::BadQueueNum);
|
||||
}
|
||||
|
||||
// If the control queue feature has been negotiated, let's increase
|
||||
// the number of queues.
|
||||
let vu_num_queues = num_queues;
|
||||
if acked_features & (1 << VIRTIO_NET_F_CTRL_VQ) != 0 {
|
||||
num_queues += 1;
|
||||
}
|
||||
// If the control queue feature has been negotiated, let's increase
|
||||
// the number of queues.
|
||||
let vu_num_queues = num_queues;
|
||||
if acked_features & (1 << VIRTIO_NET_F_CTRL_VQ) != 0 {
|
||||
num_queues += 1;
|
||||
}
|
||||
|
||||
// Make sure the virtio feature to set the MAC address is exposed to
|
||||
// the guest, even if it hasn't been negotiated with the backend.
|
||||
acked_features |= 1 << VIRTIO_NET_F_MAC;
|
||||
// Make sure the virtio feature to set the MAC address is exposed to
|
||||
// the guest, even if it hasn't been negotiated with the backend.
|
||||
acked_features |= 1 << VIRTIO_NET_F_MAC;
|
||||
|
||||
(
|
||||
acked_features,
|
||||
// If part of the available features that have been acked,
|
||||
// the PROTOCOL_FEATURES bit must be already set through
|
||||
// the VIRTIO acked features as we know the guest would
|
||||
// never ack it, thus the feature would be lost.
|
||||
acked_features & VhostUserVirtioFeatures::PROTOCOL_FEATURES.bits(),
|
||||
acked_protocol_features,
|
||||
vu_num_queues,
|
||||
config,
|
||||
)
|
||||
};
|
||||
|
||||
Ok(Net {
|
||||
id,
|
||||
common: VirtioCommon {
|
||||
device_type: VirtioDeviceType::Net as u32,
|
||||
queue_sizes: vec![vu_cfg.queue_size; num_queues],
|
||||
avail_features: acked_features,
|
||||
// If part of the available features that have been acked, the
|
||||
// PROTOCOL_FEATURES bit must be already set through the VIRTIO
|
||||
// acked features as we know the guest would never ack it, thus
|
||||
// the feature would be lost.
|
||||
acked_features: acked_features & VhostUserVirtioFeatures::PROTOCOL_FEATURES.bits(),
|
||||
avail_features,
|
||||
acked_features,
|
||||
paused_sync: Some(Arc::new(Barrier::new(2))),
|
||||
min_queues: DEFAULT_QUEUE_NUMBER as u16,
|
||||
..Default::default()
|
||||
@@ -214,28 +222,6 @@ impl Net {
|
||||
vu_num_queues: self.vu_common.vu_num_queues,
|
||||
}
|
||||
}
|
||||
|
||||
fn set_state(&mut self, state: &State) {
|
||||
self.common.avail_features = state.avail_features;
|
||||
self.common.acked_features = state.acked_features;
|
||||
self.config = state.config;
|
||||
self.vu_common.acked_protocol_features = state.acked_protocol_features;
|
||||
self.vu_common.vu_num_queues = state.vu_num_queues;
|
||||
|
||||
// The backend acknowledged features must not contain VIRTIO_NET_F_MAC
|
||||
// since we don't expect the backend to handle it.
|
||||
let backend_acked_features = self.common.acked_features & !(1 << VIRTIO_NET_F_MAC);
|
||||
|
||||
if let Err(e) = self
|
||||
.vu_common
|
||||
.restore_backend_connection(backend_acked_features)
|
||||
{
|
||||
error!(
|
||||
"Failed restoring connection with vhost-user backend: {:?}",
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Net {
|
||||
@@ -425,11 +411,6 @@ impl Snapshottable for Net {
|
||||
fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> {
|
||||
self.vu_common.snapshot(&self.id(), &self.state())
|
||||
}
|
||||
|
||||
fn restore(&mut self, snapshot: Snapshot) -> std::result::Result<(), MigratableError> {
|
||||
self.set_state(&snapshot.to_versioned_state(&self.id)?);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
impl Transportable for Net {}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user