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