xref: /cloud-hypervisor/vhost_user_net/src/lib.rs (revision 7d7bfb2034001d4cb15df2ddc56d2d350c8da30f)
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, RawFd};
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::{VhostUserBackendMut, VhostUserDaemon, VringRwLock, VringT};
26 use virtio_bindings::bindings::virtio_net::*;
27 use vm_memory::{bitmap::AtomicBitmap, GuestMemoryAtomic};
28 use vmm_sys_util::{epoll::EventSet, eventfd::EventFd};
29 
30 type GuestMemoryMmap = vm_memory::GuestMemoryMmap<AtomicBitmap>;
31 
32 pub type Result<T> = std::result::Result<T, Error>;
33 type VhostUserBackendResult<T> = std::result::Result<T, std::io::Error>;
34 
35 #[derive(Debug)]
36 pub enum Error {
37     /// Failed to create kill eventfd.
38     CreateKillEventFd(io::Error),
39     /// Failed to parse configuration string.
40     FailedConfigParse(OptionParserError),
41     /// Failed to signal used queue.
42     FailedSignalingUsedQueue(io::Error),
43     /// Failed to handle event other than input event.
44     HandleEventNotEpollIn,
45     /// Failed to handle unknown event.
46     HandleEventUnknownEvent,
47     /// Failed to open tap device.
48     OpenTap(OpenTapError),
49     /// No socket provided.
50     SocketParameterMissing,
51     /// Underlying QueuePair error.
52     NetQueuePair(net_util::NetQueuePairError),
53     /// Failed to register the TAP listener.
54     RegisterTapListener(io::Error),
55 }
56 
57 pub const SYNTAX: &str = "vhost-user-net backend parameters \
58 \"ip=<ip_addr>,mask=<net_mask>,socket=<socket_path>,client=on|off,\
59 num_queues=<number_of_queues>,queue_size=<size_of_each_queue>,tap=<if_name>\"";
60 
61 impl fmt::Display for Error {
62     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
63         write!(f, "vhost_user_net_error: {:?}", self)
64     }
65 }
66 
67 impl std::error::Error for Error {}
68 
69 impl std::convert::From<Error> for std::io::Error {
70     fn from(e: Error) -> Self {
71         std::io::Error::new(io::ErrorKind::Other, e)
72     }
73 }
74 
75 struct VhostUserNetThread {
76     net: NetQueuePair,
77     kill_evt: EventFd,
78 }
79 
80 impl VhostUserNetThread {
81     /// Create a new virtio network device with the given TAP interface.
82     fn new(tap: Tap) -> Result<Self> {
83         Ok(VhostUserNetThread {
84             kill_evt: EventFd::new(EFD_NONBLOCK).map_err(Error::CreateKillEventFd)?,
85             net: NetQueuePair {
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: 3,
95                 tap_tx_event_id: 4,
96                 rx_desc_avail: false,
97                 rx_rate_limiter: None,
98                 tx_rate_limiter: None,
99                 access_platform: None,
100             },
101         })
102     }
103 
104     pub fn set_epoll_fd(&mut self, fd: RawFd) {
105         self.net.epoll_fd = Some(fd);
106     }
107 }
108 
109 pub struct VhostUserNetBackend {
110     threads: Vec<Mutex<VhostUserNetThread>>,
111     num_queues: usize,
112     queue_size: u16,
113     queues_per_thread: Vec<u64>,
114 }
115 
116 impl VhostUserNetBackend {
117     fn new(
118         ip_addr: Ipv4Addr,
119         host_mac: MacAddr,
120         netmask: Ipv4Addr,
121         num_queues: usize,
122         queue_size: u16,
123         ifname: Option<&str>,
124     ) -> Result<Self> {
125         let mut taps = open_tap(
126             ifname,
127             Some(ip_addr),
128             Some(netmask),
129             &mut Some(host_mac),
130             num_queues / 2,
131             None,
132         )
133         .map_err(Error::OpenTap)?;
134 
135         let mut queues_per_thread = Vec::new();
136         let mut threads = Vec::new();
137         for (i, tap) in taps.drain(..).enumerate() {
138             let thread = Mutex::new(VhostUserNetThread::new(tap)?);
139             threads.push(thread);
140             queues_per_thread.push(0b11 << (i * 2));
141         }
142 
143         Ok(VhostUserNetBackend {
144             threads,
145             num_queues,
146             queue_size,
147             queues_per_thread,
148         })
149     }
150 }
151 
152 impl VhostUserBackendMut<VringRwLock<GuestMemoryAtomic<GuestMemoryMmap>>, AtomicBitmap>
153     for VhostUserNetBackend
154 {
155     fn num_queues(&self) -> usize {
156         self.num_queues
157     }
158 
159     fn max_queue_size(&self) -> usize {
160         self.queue_size as usize
161     }
162 
163     fn features(&self) -> u64 {
164         1 << VIRTIO_NET_F_GUEST_CSUM
165             | 1 << VIRTIO_NET_F_CSUM
166             | 1 << VIRTIO_NET_F_GUEST_TSO4
167             | 1 << VIRTIO_NET_F_GUEST_TSO6
168             | 1 << VIRTIO_NET_F_GUEST_ECN
169             | 1 << VIRTIO_NET_F_GUEST_UFO
170             | 1 << VIRTIO_NET_F_HOST_TSO4
171             | 1 << VIRTIO_NET_F_HOST_TSO6
172             | 1 << VIRTIO_NET_F_HOST_ECN
173             | 1 << VIRTIO_NET_F_HOST_UFO
174             | 1 << VIRTIO_NET_F_CTRL_VQ
175             | 1 << VIRTIO_NET_F_MQ
176             | 1 << VIRTIO_NET_F_MAC
177             | 1 << VIRTIO_F_NOTIFY_ON_EMPTY
178             | 1 << VIRTIO_F_VERSION_1
179             | VhostUserVirtioFeatures::PROTOCOL_FEATURES.bits()
180     }
181 
182     fn protocol_features(&self) -> VhostUserProtocolFeatures {
183         VhostUserProtocolFeatures::MQ
184             | VhostUserProtocolFeatures::REPLY_ACK
185             | VhostUserProtocolFeatures::CONFIGURE_MEM_SLOTS
186     }
187 
188     fn set_event_idx(&mut self, _enabled: bool) {}
189 
190     fn handle_event(
191         &mut self,
192         device_event: u16,
193         _evset: EventSet,
194         vrings: &[VringRwLock<GuestMemoryAtomic<GuestMemoryMmap>>],
195         thread_id: usize,
196     ) -> VhostUserBackendResult<bool> {
197         let mut thread = self.threads[thread_id].lock().unwrap();
198         match device_event {
199             0 => {
200                 if !thread.net.rx_tap_listening {
201                     net_util::register_listener(
202                         thread.net.epoll_fd.unwrap(),
203                         thread.net.tap.as_raw_fd(),
204                         epoll::Events::EPOLLIN,
205                         u64::from(thread.net.tap_rx_event_id),
206                     )
207                     .map_err(Error::RegisterTapListener)?;
208                     thread.net.rx_tap_listening = true;
209                 }
210             }
211             1 | 4 => {
212                 let mut vring = vrings[1].get_mut();
213                 if thread
214                     .net
215                     .process_tx(vring.get_queue_mut())
216                     .map_err(Error::NetQueuePair)?
217                 {
218                     vring
219                         .signal_used_queue()
220                         .map_err(Error::FailedSignalingUsedQueue)?
221                 }
222             }
223             3 => {
224                 let mut vring = vrings[0].get_mut();
225                 if thread
226                     .net
227                     .process_rx(vring.get_queue_mut())
228                     .map_err(Error::NetQueuePair)?
229                 {
230                     vring
231                         .signal_used_queue()
232                         .map_err(Error::FailedSignalingUsedQueue)?
233                 }
234             }
235             _ => return Err(Error::HandleEventUnknownEvent.into()),
236         }
237 
238         Ok(false)
239     }
240 
241     fn exit_event(&self, thread_index: usize) -> Option<EventFd> {
242         Some(
243             self.threads[thread_index]
244                 .lock()
245                 .unwrap()
246                 .kill_evt
247                 .try_clone()
248                 .unwrap(),
249         )
250     }
251 
252     fn queues_per_thread(&self) -> Vec<u64> {
253         self.queues_per_thread.clone()
254     }
255 
256     fn update_memory(
257         &mut self,
258         _mem: GuestMemoryAtomic<GuestMemoryMmap>,
259     ) -> VhostUserBackendResult<()> {
260         Ok(())
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 = VhostUserDaemon::new(
356         "vhost-user-net-backend".to_string(),
357         net_backend.clone(),
358         GuestMemoryAtomic::new(GuestMemoryMmap::new()),
359     )
360     .unwrap();
361 
362     let epoll_handlers = net_daemon.get_epoll_handlers();
363     if epoll_handlers.len() != net_backend.read().unwrap().threads.len() {
364         error!("Number of vring workers must be identical to the number of backend threads");
365         process::exit(1);
366     }
367 
368     for (index, thread) in net_backend.read().unwrap().threads.iter().enumerate() {
369         thread
370             .lock()
371             .unwrap()
372             .set_epoll_fd(epoll_handlers[index].as_raw_fd());
373     }
374 
375     if let Err(e) = if backend_config.client {
376         net_daemon.start_client(&backend_config.socket)
377     } else {
378         net_daemon.start(Listener::new(&backend_config.socket, true).unwrap())
379     } {
380         error!(
381             "failed to start daemon for vhost-user-net with error: {:?}",
382             e
383         );
384         process::exit(1);
385     }
386 
387     if let Err(e) = net_daemon.wait() {
388         error!("Error from the main thread: {:?}", e);
389     }
390 
391     for thread in net_backend.read().unwrap().threads.iter() {
392         if let Err(e) = thread.lock().unwrap().kill_evt.write(1) {
393             error!("Error shutting down worker thread: {:?}", e)
394         }
395     }
396 }
397