xref: /cloud-hypervisor/vhost_user_net/src/lib.rs (revision f7f2f25a574b1b2dba22c094fc8226d404157d15)
1 // Copyright 2019 Intel Corporation. All Rights Reserved.
2 //
3 // Portions Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
4 //
5 // Portions Copyright 2017 The Chromium OS Authors. All rights reserved.
6 //
7 // SPDX-License-Identifier: (Apache-2.0 AND BSD-3-Clause)
8 
9 use libc::{self, EFD_NONBLOCK};
10 use log::*;
11 use net_util::{
12     open_tap, MacAddr, NetCounters, NetQueuePair, OpenTapError, RxVirtio, Tap, TxVirtio,
13 };
14 use option_parser::Toggle;
15 use option_parser::{OptionParser, OptionParserError};
16 use std::fmt;
17 use std::io::{self};
18 use std::net::Ipv4Addr;
19 use std::os::unix::io::AsRawFd;
20 use std::process;
21 use std::sync::{Arc, Mutex, RwLock};
22 use std::vec::Vec;
23 use vhost::vhost_user::message::*;
24 use vhost::vhost_user::Listener;
25 use vhost_user_backend::{GuestMemoryMmap, VhostUserBackend, VhostUserDaemon, Vring, VringWorker};
26 use virtio_bindings::bindings::virtio_net::*;
27 use vm_memory::GuestMemoryAtomic;
28 use vmm_sys_util::eventfd::EventFd;
29 
30 pub type Result<T> = std::result::Result<T, Error>;
31 type VhostUserBackendResult<T> = std::result::Result<T, std::io::Error>;
32 
33 #[derive(Debug)]
34 pub enum Error {
35     /// Failed to create kill eventfd
36     CreateKillEventFd(io::Error),
37     /// Failed to parse configuration string
38     FailedConfigParse(OptionParserError),
39     /// Failed to signal used queue.
40     FailedSignalingUsedQueue(io::Error),
41     /// Failed to handle event other than input event.
42     HandleEventNotEpollIn,
43     /// Failed to handle unknown event.
44     HandleEventUnknownEvent,
45     /// Open tap device failed.
46     OpenTap(OpenTapError),
47     /// No socket provided
48     SocketParameterMissing,
49     /// Underlying QueuePair error
50     NetQueuePair(net_util::NetQueuePairError),
51     /// Failed registering the TAP listener
52     RegisterTapListener(io::Error),
53 }
54 
55 pub const SYNTAX: &str = "vhost-user-net backend parameters \
56 \"ip=<ip_addr>,mask=<net_mask>,socket=<socket_path>,client=on|off,\
57 num_queues=<number_of_queues>,queue_size=<size_of_each_queue>,tap=<if_name>\"";
58 
59 impl fmt::Display for Error {
60     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
61         write!(f, "vhost_user_net_error: {:?}", self)
62     }
63 }
64 
65 impl std::error::Error for Error {}
66 
67 impl std::convert::From<Error> for std::io::Error {
68     fn from(e: Error) -> Self {
69         std::io::Error::new(io::ErrorKind::Other, e)
70     }
71 }
72 
73 struct VhostUserNetThread {
74     net: NetQueuePair,
75     kill_evt: EventFd,
76 }
77 
78 impl VhostUserNetThread {
79     /// Create a new virtio network device with the given TAP interface.
80     fn new(tap: Tap) -> Result<Self> {
81         Ok(VhostUserNetThread {
82             kill_evt: EventFd::new(EFD_NONBLOCK).map_err(Error::CreateKillEventFd)?,
83             net: NetQueuePair {
84                 mem: None,
85                 tap_for_write_epoll: tap.clone(),
86                 tap,
87                 rx: RxVirtio::new(),
88                 tx: TxVirtio::new(),
89                 rx_tap_listening: false,
90                 tx_tap_listening: false,
91                 epoll_fd: None,
92                 counters: NetCounters::default(),
93                 tap_rx_event_id: 2,
94                 tap_tx_event_id: 3,
95                 rx_desc_avail: false,
96                 rx_rate_limiter: None,
97                 tx_rate_limiter: None,
98             },
99         })
100     }
101 
102     pub fn set_vring_worker(&mut self, vring_worker: Option<Arc<VringWorker>>) {
103         self.net.epoll_fd = Some(vring_worker.as_ref().unwrap().as_raw_fd());
104     }
105 }
106 
107 pub struct VhostUserNetBackend {
108     threads: Vec<Mutex<VhostUserNetThread>>,
109     num_queues: usize,
110     queue_size: u16,
111     queues_per_thread: Vec<u64>,
112 }
113 
114 impl VhostUserNetBackend {
115     fn new(
116         ip_addr: Ipv4Addr,
117         host_mac: MacAddr,
118         netmask: Ipv4Addr,
119         num_queues: usize,
120         queue_size: u16,
121         ifname: Option<&str>,
122     ) -> Result<Self> {
123         let mut taps = open_tap(
124             ifname,
125             Some(ip_addr),
126             Some(netmask),
127             &mut Some(host_mac),
128             num_queues / 2,
129             None,
130         )
131         .map_err(Error::OpenTap)?;
132 
133         let mut queues_per_thread = Vec::new();
134         let mut threads = Vec::new();
135         for (i, tap) in taps.drain(..).enumerate() {
136             let thread = Mutex::new(VhostUserNetThread::new(tap)?);
137             threads.push(thread);
138             queues_per_thread.push(0b11 << (i * 2));
139         }
140 
141         Ok(VhostUserNetBackend {
142             threads,
143             num_queues,
144             queue_size,
145             queues_per_thread,
146         })
147     }
148 }
149 
150 impl VhostUserBackend for VhostUserNetBackend {
151     fn num_queues(&self) -> usize {
152         self.num_queues
153     }
154 
155     fn max_queue_size(&self) -> usize {
156         self.queue_size as usize
157     }
158 
159     fn features(&self) -> u64 {
160         1 << VIRTIO_NET_F_GUEST_CSUM
161             | 1 << VIRTIO_NET_F_CSUM
162             | 1 << VIRTIO_NET_F_GUEST_TSO4
163             | 1 << VIRTIO_NET_F_GUEST_TSO6
164             | 1 << VIRTIO_NET_F_GUEST_ECN
165             | 1 << VIRTIO_NET_F_GUEST_UFO
166             | 1 << VIRTIO_NET_F_HOST_TSO4
167             | 1 << VIRTIO_NET_F_HOST_TSO6
168             | 1 << VIRTIO_NET_F_HOST_ECN
169             | 1 << VIRTIO_NET_F_HOST_UFO
170             | 1 << VIRTIO_NET_F_MRG_RXBUF
171             | 1 << VIRTIO_NET_F_CTRL_VQ
172             | 1 << VIRTIO_NET_F_MQ
173             | 1 << VIRTIO_NET_F_MAC
174             | 1 << VIRTIO_F_NOTIFY_ON_EMPTY
175             | 1 << VIRTIO_F_VERSION_1
176             | VhostUserVirtioFeatures::PROTOCOL_FEATURES.bits()
177     }
178 
179     fn protocol_features(&self) -> VhostUserProtocolFeatures {
180         VhostUserProtocolFeatures::MQ
181             | VhostUserProtocolFeatures::REPLY_ACK
182             | VhostUserProtocolFeatures::CONFIGURE_MEM_SLOTS
183     }
184 
185     fn set_event_idx(&mut self, _enabled: bool) {}
186 
187     fn update_memory(&mut self, mem: GuestMemoryMmap) -> VhostUserBackendResult<()> {
188         for thread in self.threads.iter() {
189             thread.lock().unwrap().net.mem = Some(GuestMemoryAtomic::new(mem.clone()));
190         }
191         Ok(())
192     }
193 
194     fn handle_event(
195         &self,
196         device_event: u16,
197         _evset: epoll::Events,
198         vrings: &[Arc<RwLock<Vring>>],
199         thread_id: usize,
200     ) -> VhostUserBackendResult<bool> {
201         let mut thread = self.threads[thread_id].lock().unwrap();
202         match device_event {
203             0 => {
204                 if !thread.net.rx_tap_listening {
205                     net_util::register_listener(
206                         thread.net.epoll_fd.unwrap(),
207                         thread.net.tap.as_raw_fd(),
208                         epoll::Events::EPOLLIN,
209                         u64::from(thread.net.tap_rx_event_id),
210                     )
211                     .map_err(Error::RegisterTapListener)?;
212                     thread.net.rx_tap_listening = true;
213                 }
214             }
215             1 | 3 => {
216                 let mut vring = vrings[1].write().unwrap();
217                 if thread
218                     .net
219                     .process_tx(&mut vring.mut_queue())
220                     .map_err(Error::NetQueuePair)?
221                 {
222                     vring
223                         .signal_used_queue()
224                         .map_err(Error::FailedSignalingUsedQueue)?
225                 }
226             }
227             2 => {
228                 let mut vring = vrings[0].write().unwrap();
229                 if thread
230                     .net
231                     .process_rx(&mut vring.mut_queue())
232                     .map_err(Error::NetQueuePair)?
233                 {
234                     vring
235                         .signal_used_queue()
236                         .map_err(Error::FailedSignalingUsedQueue)?
237                 }
238             }
239             _ => return Err(Error::HandleEventUnknownEvent.into()),
240         }
241 
242         Ok(false)
243     }
244 
245     fn exit_event(&self, thread_index: usize) -> Option<(EventFd, Option<u16>)> {
246         // The exit event is placed after the queues and the tap event, which
247         // is event index 3.
248         Some((
249             self.threads[thread_index]
250                 .lock()
251                 .unwrap()
252                 .kill_evt
253                 .try_clone()
254                 .unwrap(),
255             Some(3),
256         ))
257     }
258 
259     fn queues_per_thread(&self) -> Vec<u64> {
260         self.queues_per_thread.clone()
261     }
262 }
263 
264 pub struct VhostUserNetBackendConfig {
265     pub ip: Ipv4Addr,
266     pub host_mac: MacAddr,
267     pub mask: Ipv4Addr,
268     pub socket: String,
269     pub num_queues: usize,
270     pub queue_size: u16,
271     pub tap: Option<String>,
272     pub client: bool,
273 }
274 
275 impl VhostUserNetBackendConfig {
276     pub fn parse(backend: &str) -> Result<Self> {
277         let mut parser = OptionParser::new();
278 
279         parser
280             .add("tap")
281             .add("ip")
282             .add("host_mac")
283             .add("mask")
284             .add("queue_size")
285             .add("num_queues")
286             .add("socket")
287             .add("client");
288 
289         parser.parse(backend).map_err(Error::FailedConfigParse)?;
290 
291         let tap = parser.get("tap");
292         let ip = parser
293             .convert("ip")
294             .map_err(Error::FailedConfigParse)?
295             .unwrap_or_else(|| Ipv4Addr::new(192, 168, 100, 1));
296         let host_mac = parser
297             .convert("host_mac")
298             .map_err(Error::FailedConfigParse)?
299             .unwrap_or_else(MacAddr::local_random);
300         let mask = parser
301             .convert("mask")
302             .map_err(Error::FailedConfigParse)?
303             .unwrap_or_else(|| Ipv4Addr::new(255, 255, 255, 0));
304         let queue_size = parser
305             .convert("queue_size")
306             .map_err(Error::FailedConfigParse)?
307             .unwrap_or(256);
308         let num_queues = parser
309             .convert("num_queues")
310             .map_err(Error::FailedConfigParse)?
311             .unwrap_or(2);
312         let socket = parser.get("socket").ok_or(Error::SocketParameterMissing)?;
313         let client = parser
314             .convert::<Toggle>("client")
315             .map_err(Error::FailedConfigParse)?
316             .unwrap_or(Toggle(false))
317             .0;
318 
319         Ok(VhostUserNetBackendConfig {
320             ip,
321             host_mac,
322             mask,
323             socket,
324             num_queues,
325             queue_size,
326             tap,
327             client,
328         })
329     }
330 }
331 
332 pub fn start_net_backend(backend_command: &str) {
333     let backend_config = match VhostUserNetBackendConfig::parse(backend_command) {
334         Ok(config) => config,
335         Err(e) => {
336             eprintln!("Failed parsing parameters {:?}", e);
337             process::exit(1);
338         }
339     };
340 
341     let tap = backend_config.tap.as_deref();
342 
343     let net_backend = Arc::new(RwLock::new(
344         VhostUserNetBackend::new(
345             backend_config.ip,
346             backend_config.host_mac,
347             backend_config.mask,
348             backend_config.num_queues,
349             backend_config.queue_size,
350             tap,
351         )
352         .unwrap(),
353     ));
354 
355     let mut net_daemon =
356         VhostUserDaemon::new("vhost-user-net-backend".to_string(), net_backend.clone()).unwrap();
357 
358     let mut vring_workers = net_daemon.get_vring_workers();
359 
360     if vring_workers.len() != net_backend.read().unwrap().threads.len() {
361         error!("Number of vring workers must be identical to the number of backend threads");
362         process::exit(1);
363     }
364 
365     for thread in net_backend.read().unwrap().threads.iter() {
366         thread
367             .lock()
368             .unwrap()
369             .set_vring_worker(Some(vring_workers.remove(0)));
370     }
371 
372     if let Err(e) = if backend_config.client {
373         net_daemon.start_client(&backend_config.socket)
374     } else {
375         net_daemon.start_server(Listener::new(&backend_config.socket, true).unwrap())
376     } {
377         error!(
378             "failed to start daemon for vhost-user-net with error: {:?}",
379             e
380         );
381         process::exit(1);
382     }
383 
384     if let Err(e) = net_daemon.wait() {
385         error!("Error from the main thread: {:?}", e);
386     }
387 
388     for thread in net_backend.read().unwrap().threads.iter() {
389         if let Err(e) = thread.lock().unwrap().kill_evt.write(1) {
390             error!("Error shutting down worker thread: {:?}", e)
391         }
392     }
393 }
394