Path: blob/main/devices/src/virtio/vhost_user_backend/net/sys/windows.rs
5394 views
// Copyright 2022 The ChromiumOS Authors1// Use of this source code is governed by a BSD-style license that can be2// found in the LICENSE file.34use std::sync::Arc;56use anyhow::bail;7use anyhow::Context;8use argh::FromArgs;9use base::error;10use base::info;11use base::named_pipes::OverlappedWrapper;12use base::named_pipes::PipeConnection;13use base::warn;14use base::Event;15use base::RawDescriptor;16use cros_async::EventAsync;17use cros_async::Executor;18use cros_async::IntoAsync;19use cros_async::IoSource;20use futures::channel::oneshot;21use futures::future::AbortHandle;22use futures::future::Abortable;23use futures::pin_mut;24use futures::select_biased;25use futures::FutureExt;26use hypervisor::ProtectionType;27#[cfg(feature = "slirp")]28use net_util::Slirp;29use net_util::TapT;30use proc_init::common_child_setup;31use proc_init::CommonChildStartupArgs;32#[cfg(feature = "slirp")]33use serde::Deserialize;34#[cfg(feature = "slirp")]35use serde::Serialize;36use sync::Mutex;37use tube_transporter::TubeToken;38use virtio_sys::virtio_net;39use vm_memory::GuestMemory;40use vmm_vhost::VHOST_USER_F_PROTOCOL_FEATURES;4142use crate::virtio;43use crate::virtio::base_features;44use crate::virtio::net::process_rx;45use crate::virtio::net::NetError;46#[cfg(feature = "slirp")]47use crate::virtio::net::MAX_BUFFER_SIZE;48use crate::virtio::vhost_user_backend::handler::sys::windows::read_from_tube_transporter;49use crate::virtio::vhost_user_backend::handler::sys::windows::run_handler;50use crate::virtio::vhost_user_backend::handler::DeviceRequestHandler;51use crate::virtio::vhost_user_backend::handler::VhostUserDevice;52use crate::virtio::vhost_user_backend::handler::WorkerState;53use crate::virtio::vhost_user_backend::net::run_ctrl_queue;54use crate::virtio::vhost_user_backend::net::run_tx_queue;55use crate::virtio::vhost_user_backend::net::NetBackend;56use crate::virtio::vhost_user_backend::net::NET_EXECUTOR;57use crate::virtio::Interrupt;58use crate::virtio::Queue;5960impl<T: 'static> NetBackend<T>61where62T: TapT + IntoAsync,63{64#[cfg(feature = "slirp")]65pub fn new_slirp(66guest_pipe: PipeConnection,67slirp_kill_event: Event,68) -> anyhow::Result<NetBackend<Slirp>> {69let avail_features = base_features(ProtectionType::Unprotected)70| 1 << virtio_net::VIRTIO_NET_F_CTRL_VQ71| 1 << VHOST_USER_F_PROTOCOL_FEATURES;72let slirp = Slirp::new_for_multi_process(guest_pipe).map_err(NetError::SlirpCreateError)?;7374Ok(NetBackend::<Slirp> {75tap: slirp,76avail_features,77acked_features: 0,78mtu: 1500,79slirp_kill_event,80workers: Default::default(),81})82}83}8485async fn run_rx_queue<T: TapT>(86mut queue: Queue,87mut tap: IoSource<T>,88kick_evt: EventAsync,89read_notifier: EventAsync,90mut overlapped_wrapper: OverlappedWrapper,91mut stop_rx: oneshot::Receiver<()>,92) -> Queue {93let mut rx_buf = [0u8; MAX_BUFFER_SIZE];94let mut rx_count = 0;95let mut deferred_rx = false;9697// SAFETY: safe because rx_buf & overlapped_wrapper live until the98// overlapped operation completes and are not used in any other operations99// until that time.100unsafe {101tap.as_source_mut()102.read_overlapped(&mut rx_buf, &mut overlapped_wrapper)103.expect("read_overlapped failed")104};105106let read_notifier_future = read_notifier.next_val().fuse();107pin_mut!(read_notifier_future);108let kick_evt_future = kick_evt.next_val().fuse();109pin_mut!(kick_evt_future);110111loop {112// If we already have a packet from deferred RX, we don't need to wait for the slirp device.113if !deferred_rx {114select_biased! {115read_notifier_res = read_notifier_future => {116read_notifier_future.set(read_notifier.next_val().fuse());117if let Err(e) = read_notifier_res {118error!("Failed to wait for tap device to become readable: {}", e);119break;120}121}122_ = stop_rx => {123break;124}125}126if let Err(e) = read_notifier.next_val().await {127error!("Failed to wait for tap device to become readable: {}", e);128break;129}130}131132let needs_interrupt = process_rx(133&mut queue,134tap.as_source_mut(),135&mut rx_buf,136&mut deferred_rx,137&mut rx_count,138&mut overlapped_wrapper,139);140if needs_interrupt {141queue.trigger_interrupt();142}143144// There aren't any RX descriptors available for us to write packets to. Wait for the guest145// to consume some packets and make more descriptors available to us.146if deferred_rx {147select_biased! {148kick = kick_evt_future => {149kick_evt_future.set(kick_evt.next_val().fuse());150if let Err(e) = kick {151error!("Failed to read kick event for rx queue: {}", e);152break;153}154}155_ = stop_rx => {156break;157}158}159}160}161162queue163}164165/// Platform specific impl of VhostUserDevice::start_queue.166pub(in crate::virtio::vhost_user_backend::net) fn start_queue<T: 'static + IntoAsync + TapT>(167backend: &mut NetBackend<T>,168idx: usize,169queue: virtio::Queue,170_mem: GuestMemory,171) -> anyhow::Result<()> {172if backend.workers.get(idx).is_some() {173warn!("Starting new queue handler without stopping old handler");174backend.stop_queue(idx);175}176177let overlapped_wrapper =178OverlappedWrapper::new(true).expect("Failed to create overlapped wrapper");179180super::super::NET_EXECUTOR.with(|ex| {181// Safe because the executor is initialized in main() below.182let ex = ex.get().expect("Executor not initialized");183184let kick_evt = queue185.event()186.try_clone()187.context("failed to clone queue event")?;188let kick_evt =189EventAsync::new(kick_evt, ex).context("failed to create EventAsync for kick_evt")?;190let tap = backend191.tap192.try_clone()193.context("failed to clone tap device")?;194let worker_tuple = match idx {1950 => {196let tap = ex197.async_from(tap)198.context("failed to create async tap device")?;199let read_notifier = overlapped_wrapper200.get_h_event_ref()201.unwrap()202.try_clone()203.unwrap();204let read_notifier = EventAsync::new_without_reset(read_notifier, ex)205.context("failed to create async read notifier")?;206207let (stop_tx, stop_rx) = futures::channel::oneshot::channel();208(209ex.spawn_local(run_rx_queue(210queue,211tap,212kick_evt,213read_notifier,214overlapped_wrapper,215stop_rx,216)),217stop_tx,218)219}2201 => {221let (stop_tx, stop_rx) = futures::channel::oneshot::channel();222(223ex.spawn_local(run_tx_queue(queue, tap, kick_evt, stop_rx)),224stop_tx,225)226}2272 => {228let (stop_tx, stop_rx) = futures::channel::oneshot::channel();229(230ex.spawn_local(run_ctrl_queue(231queue,232tap,233kick_evt,234backend.acked_features,2351, /* vq_pairs */236stop_rx,237)),238stop_tx,239)240}241_ => bail!("attempted to start unknown queue: {}", idx),242};243244backend.workers[idx] = Some(worker_tuple);245Ok(())246})247}248249#[cfg(feature = "slirp")]250impl<T> Drop for NetBackend<T>251where252T: TapT + IntoAsync,253{254fn drop(&mut self) {255let _ = self.slirp_kill_event.signal();256}257}258259/// Config arguments passed through the bootstrap Tube from the broker to the Net backend260/// process.261#[cfg(feature = "slirp")]262#[derive(Serialize, Deserialize, Debug)]263pub struct NetBackendConfig {264pub guest_pipe: PipeConnection,265pub slirp_kill_event: Event,266}267268#[derive(FromArgs, Debug)]269#[argh(subcommand, name = "net", description = "")]270pub struct Options {271#[argh(272option,273description = "pipe handle end for Tube Transporter",274arg_name = "HANDLE"275)]276bootstrap: usize,277}278279#[cfg(all(windows, not(feature = "slirp")))]280compile_error!("vhost-user net device requires slirp feature on Windows.");281282#[cfg(feature = "slirp")]283pub fn start_device(opts: Options) -> anyhow::Result<()> {284// Get the Tubes from the TubeTransporter. Then get the "Config" from the bootstrap_tube285// which will contain slirp settings.286let raw_transport_tube = opts.bootstrap as RawDescriptor;287288let mut tubes = read_from_tube_transporter(raw_transport_tube).unwrap();289290let vhost_user_tube = tubes.get_tube(TubeToken::VhostUser).unwrap();291let bootstrap_tube = tubes.get_tube(TubeToken::Bootstrap).unwrap();292293let startup_args: CommonChildStartupArgs =294bootstrap_tube.recv::<CommonChildStartupArgs>().unwrap();295let _child_cleanup = common_child_setup(startup_args).unwrap();296297let net_backend_config = bootstrap_tube.recv::<NetBackendConfig>().unwrap();298299let exit_event = bootstrap_tube.recv::<Event>()?;300301// We only have one net device for now.302let dev = NetBackend::<net_util::Slirp>::new_slirp(303net_backend_config.guest_pipe,304net_backend_config.slirp_kill_event,305)306.unwrap();307308let handler = DeviceRequestHandler::new(dev);309310let ex = Executor::new().context("failed to create executor")?;311312NET_EXECUTOR.with(|net_ex| {313let _ = net_ex.set(ex.clone());314});315316// TODO(b/213170185): Uncomment once sandbox is upstreamed.317// if sandbox::is_sandbox_target() {318// sandbox::TargetServices::get()319// .expect("failed to get target services")320// .unwrap()321// .lower_token();322// }323324info!("vhost-user net device ready, starting run loop...");325ex.run_until(run_handler(326Box::new(handler),327vhost_user_tube,328exit_event,329&ex,330))331.context("run_until error")?332.context("run_handler error")333}334335336