1 // Copyright 2021 The Chromium OS Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #[cfg_attr(windows, path = "windows/net.rs")]
6 #[cfg_attr(not(windows), path = "unix/net.rs")]
7 mod net;
8
9 // Only Windows exposes public symbols, but the module level use is used on both platforms.
10 #[allow(unused_imports)]
11 pub use net::*;
12
13 use std::sync::Arc;
14
15 use anyhow::{anyhow, bail, Context};
16 use base::{error, Event};
17 use cros_async::{EventAsync, Executor, IntoAsync};
18 use data_model::DataInit;
19 use futures::future::AbortHandle;
20 use net_util::TapT;
21 use once_cell::sync::OnceCell;
22 use sync::Mutex;
23 use vm_memory::GuestMemory;
24 use vmm_vhost::message::VhostUserProtocolFeatures;
25
26 use crate::virtio;
27 use crate::virtio::net::{build_config, process_ctrl, process_tx, virtio_features_to_tap_offload};
28 use crate::virtio::vhost::user::device::handler::{Doorbell, VhostUserBackend};
29
30 thread_local! {
31 pub(crate) static NET_EXECUTOR: OnceCell<Executor> = OnceCell::new();
32 }
33
34 // TODO(b/188947559): Come up with better way to include these constants. Compiler errors happen
35 // if they are kept in the trait.
36 const MAX_QUEUE_NUM: usize = 3; /* rx, tx, ctrl */
37 const MAX_VRING_LEN: u16 = 256;
38
run_tx_queue<T: TapT>( mut queue: virtio::Queue, mem: GuestMemory, mut tap: T, doorbell: Arc<Mutex<Doorbell>>, kick_evt: EventAsync, )39 async fn run_tx_queue<T: TapT>(
40 mut queue: virtio::Queue,
41 mem: GuestMemory,
42 mut tap: T,
43 doorbell: Arc<Mutex<Doorbell>>,
44 kick_evt: EventAsync,
45 ) {
46 loop {
47 if let Err(e) = kick_evt.next_val().await {
48 error!("Failed to read kick event for tx queue: {}", e);
49 break;
50 }
51
52 process_tx(&doorbell, &mut queue, &mem, &mut tap);
53 }
54 }
55
run_ctrl_queue<T: TapT>( mut queue: virtio::Queue, mem: GuestMemory, mut tap: T, doorbell: Arc<Mutex<Doorbell>>, kick_evt: EventAsync, acked_features: u64, vq_pairs: u16, )56 async fn run_ctrl_queue<T: TapT>(
57 mut queue: virtio::Queue,
58 mem: GuestMemory,
59 mut tap: T,
60 doorbell: Arc<Mutex<Doorbell>>,
61 kick_evt: EventAsync,
62 acked_features: u64,
63 vq_pairs: u16,
64 ) {
65 loop {
66 if let Err(e) = kick_evt.next_val().await {
67 error!("Failed to read kick event for tx queue: {}", e);
68 break;
69 }
70
71 if let Err(e) = process_ctrl(
72 &doorbell,
73 &mut queue,
74 &mem,
75 &mut tap,
76 acked_features,
77 vq_pairs,
78 ) {
79 error!("Failed to process ctrl queue: {}", e);
80 break;
81 }
82 }
83 }
84
85 pub(crate) struct NetBackend<T: TapT + IntoAsync> {
86 tap: T,
87 avail_features: u64,
88 acked_features: u64,
89 acked_protocol_features: VhostUserProtocolFeatures,
90 workers: [Option<AbortHandle>; MAX_QUEUE_NUM],
91 mtu: u16,
92 #[cfg(all(windows, feature = "slirp"))]
93 slirp_kill_event: Event,
94 }
95
96 impl<T: 'static> NetBackend<T>
97 where
98 T: TapT + IntoAsync,
99 {
max_vq_pairs() -> usize100 fn max_vq_pairs() -> usize {
101 Self::MAX_QUEUE_NUM / 2
102 }
103 }
104
105 impl<T: 'static> VhostUserBackend for NetBackend<T>
106 where
107 T: TapT + IntoAsync,
108 {
109 const MAX_QUEUE_NUM: usize = MAX_QUEUE_NUM; /* rx, tx, ctrl */
110 const MAX_VRING_LEN: u16 = MAX_VRING_LEN;
111
112 type Error = anyhow::Error;
113
features(&self) -> u64114 fn features(&self) -> u64 {
115 self.avail_features
116 }
117
ack_features(&mut self, value: u64) -> anyhow::Result<()>118 fn ack_features(&mut self, value: u64) -> anyhow::Result<()> {
119 let unrequested_features = value & !self.avail_features;
120 if unrequested_features != 0 {
121 bail!("invalid features are given: {:#x}", unrequested_features);
122 }
123
124 self.acked_features |= value;
125
126 self.tap
127 .set_offload(virtio_features_to_tap_offload(self.acked_features))
128 .context("failed to set tap offload to match features")?;
129
130 Ok(())
131 }
132
acked_features(&self) -> u64133 fn acked_features(&self) -> u64 {
134 self.acked_features
135 }
136
protocol_features(&self) -> VhostUserProtocolFeatures137 fn protocol_features(&self) -> VhostUserProtocolFeatures {
138 VhostUserProtocolFeatures::CONFIG
139 }
140
ack_protocol_features(&mut self, features: u64) -> anyhow::Result<()>141 fn ack_protocol_features(&mut self, features: u64) -> anyhow::Result<()> {
142 let features = VhostUserProtocolFeatures::from_bits(features)
143 .ok_or_else(|| anyhow!("invalid protocol features are given: {:#x}", features))?;
144 let supported = self.protocol_features();
145 self.acked_protocol_features = features & supported;
146 Ok(())
147 }
148
acked_protocol_features(&self) -> u64149 fn acked_protocol_features(&self) -> u64 {
150 self.acked_protocol_features.bits()
151 }
152
read_config(&self, offset: u64, data: &mut [u8])153 fn read_config(&self, offset: u64, data: &mut [u8]) {
154 let config_space = build_config(Self::max_vq_pairs() as u16, self.mtu);
155 virtio::copy_config(data, 0, config_space.as_slice(), offset);
156 }
157
reset(&mut self)158 fn reset(&mut self) {}
159
start_queue( &mut self, idx: usize, queue: virtio::Queue, mem: GuestMemory, doorbell: Arc<Mutex<Doorbell>>, kick_evt: Event, ) -> anyhow::Result<()>160 fn start_queue(
161 &mut self,
162 idx: usize,
163 queue: virtio::Queue,
164 mem: GuestMemory,
165 doorbell: Arc<Mutex<Doorbell>>,
166 kick_evt: Event,
167 ) -> anyhow::Result<()> {
168 net::start_queue(self, idx, queue, mem, doorbell, kick_evt)
169 }
170
stop_queue(&mut self, idx: usize)171 fn stop_queue(&mut self, idx: usize) {
172 if let Some(handle) = self.workers.get_mut(idx).and_then(Option::take) {
173 handle.abort();
174 }
175 }
176 }
177
178 /// Starts a vhost-user net device.
run_net_device(program_name: &str, args: &[&str]) -> anyhow::Result<()>179 pub fn run_net_device(program_name: &str, args: &[&str]) -> anyhow::Result<()> {
180 start_device(program_name, args)
181 }
182