Path: blob/main/devices/src/virtio/vhost_user_backend/net.rs
5394 views
// Copyright 2021 The ChromiumOS Authors1// Use of this source code is governed by a BSD-style license that can be2// found in the LICENSE file.34pub mod sys;56use std::cell::OnceCell;78use anyhow::anyhow;9use anyhow::Context;10use base::error;11use base::AsRawDescriptors;12use cros_async::EventAsync;13use cros_async::Executor;14use cros_async::IntoAsync;15use cros_async::TaskHandle;16use futures::channel::oneshot;17use futures::pin_mut;18use futures::select_biased;19use futures::FutureExt;20use net_util::TapT;21use serde::Deserialize;22use serde::Serialize;23use snapshot::AnySnapshot;24pub use sys::start_device as run_net_device;25pub use sys::Options;26use vm_memory::GuestMemory;27use vmm_vhost::message::VhostUserProtocolFeatures;28use zerocopy::IntoBytes;2930use crate::virtio;31use crate::virtio::net::build_config;32use crate::virtio::net::process_ctrl;33use crate::virtio::net::process_tx;34use crate::virtio::net::virtio_features_to_tap_offload;35use crate::virtio::vhost_user_backend::handler::DeviceRequestHandler;36use crate::virtio::vhost_user_backend::handler::Error as DeviceError;37use crate::virtio::vhost_user_backend::handler::VhostUserDevice;38use crate::virtio::vhost_user_backend::VhostUserDeviceBuilder;39use crate::virtio::Queue;4041thread_local! {42pub(crate) static NET_EXECUTOR: OnceCell<Executor> = const { OnceCell::new() };43}4445// TODO(b/188947559): Come up with better way to include these constants. Compiler errors happen46// if they are kept in the trait.47const MAX_QUEUE_NUM: usize = 3; /* rx, tx, ctrl */4849async fn run_tx_queue<T: TapT>(50mut queue: Queue,51mut tap: T,52kick_evt: EventAsync,53mut stop_rx: oneshot::Receiver<()>,54) -> Queue {55let kick_evt_future = kick_evt.next_val().fuse();56pin_mut!(kick_evt_future);57loop {58select_biased! {59kick = kick_evt_future => {60kick_evt_future.set(kick_evt.next_val().fuse());61if let Err(e) = kick {62error!("Failed to read kick event for tx queue: {}", e);63break;64}65}66_ = stop_rx => {67break;68}69}7071process_tx(&mut queue, &mut tap);72}73queue74}7576async fn run_ctrl_queue<T: TapT>(77mut queue: Queue,78mut tap: T,79kick_evt: EventAsync,80acked_features: u64,81vq_pairs: u16,82mut stop_rx: oneshot::Receiver<()>,83) -> Queue {84let kick_evt_future = kick_evt.next_val().fuse();85pin_mut!(kick_evt_future);86loop {87select_biased! {88kick = kick_evt_future => {89kick_evt_future.set(kick_evt.next_val().fuse());90if let Err(e) = kick {91error!("Failed to read kick event for tx queue: {}", e);92break;93}94}95_ = stop_rx => {96break;97}98}99100if let Err(e) = process_ctrl(&mut queue, &mut tap, acked_features, vq_pairs) {101error!("Failed to process ctrl queue: {}", e);102break;103}104}105queue106}107108pub struct NetBackend<T: TapT + IntoAsync> {109tap: T,110avail_features: u64,111acked_features: u64,112mtu: u16,113#[cfg(all(windows, feature = "slirp"))]114slirp_kill_event: base::Event,115workers: [Option<(TaskHandle<Queue>, oneshot::Sender<()>)>; MAX_QUEUE_NUM],116}117118#[derive(Serialize, Deserialize)]119pub struct NetBackendSnapshot {120acked_feature: u64,121}122123impl<T: 'static> NetBackend<T>124where125T: TapT + IntoAsync,126{127fn max_vq_pairs() -> usize {128MAX_QUEUE_NUM / 2129}130}131132impl<T: 'static> AsRawDescriptors for NetBackend<T>133where134T: TapT + IntoAsync + AsRawDescriptors,135{136fn as_raw_descriptors(&self) -> Vec<base::RawDescriptor> {137self.tap.as_raw_descriptors()138}139}140141impl<T: 'static> VhostUserDevice for NetBackend<T>142where143T: TapT + IntoAsync,144{145fn max_queue_num(&self) -> usize {146MAX_QUEUE_NUM147}148149fn features(&self) -> u64 {150self.avail_features151}152153fn ack_features(&mut self, value: u64) -> anyhow::Result<()> {154self.acked_features |= value;155156self.tap157.set_offload(virtio_features_to_tap_offload(self.acked_features))158.context("failed to set tap offload to match features")?;159160Ok(())161}162163fn protocol_features(&self) -> VhostUserProtocolFeatures {164VhostUserProtocolFeatures::CONFIG | VhostUserProtocolFeatures::DEVICE_STATE165}166167fn read_config(&self, offset: u64, data: &mut [u8]) {168let config_space = build_config(Self::max_vq_pairs() as u16, self.mtu, None);169virtio::copy_config(data, 0, config_space.as_bytes(), offset);170}171172fn reset(&mut self) {}173174fn start_queue(175&mut self,176idx: usize,177queue: virtio::Queue,178mem: GuestMemory,179) -> anyhow::Result<()> {180sys::start_queue(self, idx, queue, mem)181}182183fn stop_queue(&mut self, idx: usize) -> anyhow::Result<virtio::Queue> {184if let Some((task, stop_tx)) = self.workers.get_mut(idx).and_then(Option::take) {185if stop_tx.send(()).is_err() {186return Err(anyhow!("Failed to request stop for net queue future"));187}188189// Wait for queue_task to be aborted.190let queue = NET_EXECUTOR191.with(|ex| {192let ex = ex.get().expect("Executor not initialized");193ex.run_until(task)194})195.context("Failed to resolve queue worker future")?;196197Ok(queue)198} else {199Err(anyhow::Error::new(DeviceError::WorkerNotFound))200}201}202203fn enter_suspended_state(&mut self) -> anyhow::Result<()> {204// No non-queue workers.205Ok(())206}207208fn snapshot(&mut self) -> anyhow::Result<AnySnapshot> {209AnySnapshot::to_any(NetBackendSnapshot {210acked_feature: self.acked_features,211})212.context("Failed to serialize NetBackendSnapshot")213}214215fn restore(&mut self, data: AnySnapshot) -> anyhow::Result<()> {216let net_backend_snapshot: NetBackendSnapshot =217AnySnapshot::from_any(data).context("Failed to deserialize NetBackendSnapshot")?;218self.acked_features = net_backend_snapshot.acked_feature;219Ok(())220}221}222223impl<T> VhostUserDeviceBuilder for NetBackend<T>224where225T: TapT + IntoAsync + 'static,226{227fn build(self: Box<Self>, ex: &Executor) -> anyhow::Result<Box<dyn vmm_vhost::Backend>> {228NET_EXECUTOR.with(|thread_ex| {229let _ = thread_ex.set(ex.clone());230});231let handler = DeviceRequestHandler::new(*self);232233Ok(Box::new(handler))234}235}236237238