xref: /cloud-hypervisor/virtio-devices/src/vhost_user/net.rs (revision 9af2968a7dc47b89bf07ea9dc5e735084efcfa3a)
1 // Copyright 2019 Intel Corporation. All Rights Reserved.
2 // SPDX-License-Identifier: Apache-2.0
3 
4 use crate::seccomp_filters::{get_seccomp_filter, Thread};
5 use crate::vhost_user::vu_common_ctrl::{
6     add_memory_region, connect_vhost_user, negotiate_features_vhost_user, reset_vhost_user,
7     setup_vhost_user, update_mem_table, VhostUserConfig,
8 };
9 use crate::vhost_user::{Error, Inflight, Result, VhostUserEpollHandler};
10 use crate::{
11     ActivateError, ActivateResult, EpollHelper, EpollHelperError, EpollHelperHandler, Queue,
12     VirtioCommon, VirtioDevice, VirtioDeviceType, VirtioInterrupt, EPOLL_HELPER_EVENT_LAST,
13     VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_VERSION_1,
14 };
15 use crate::{GuestMemoryMmap, GuestRegionMmap};
16 use net_util::{build_net_config_space, CtrlQueue, MacAddr, VirtioNetConfig};
17 use seccomp::{SeccompAction, SeccompFilter};
18 use std::ops::Deref;
19 use std::os::unix::io::AsRawFd;
20 use std::result;
21 use std::sync::atomic::AtomicBool;
22 use std::sync::{Arc, Barrier, Mutex};
23 use std::thread;
24 use std::vec::Vec;
25 use vhost::vhost_user::message::{VhostUserProtocolFeatures, VhostUserVirtioFeatures};
26 use vhost::vhost_user::{Master, MasterReqHandler, VhostUserMaster, VhostUserMasterReqHandler};
27 use virtio_bindings::bindings::virtio_net::{
28     VIRTIO_NET_F_CSUM, VIRTIO_NET_F_CTRL_VQ, VIRTIO_NET_F_GUEST_CSUM, VIRTIO_NET_F_GUEST_ECN,
29     VIRTIO_NET_F_GUEST_TSO4, VIRTIO_NET_F_GUEST_TSO6, VIRTIO_NET_F_GUEST_UFO,
30     VIRTIO_NET_F_HOST_ECN, VIRTIO_NET_F_HOST_TSO4, VIRTIO_NET_F_HOST_TSO6, VIRTIO_NET_F_HOST_UFO,
31     VIRTIO_NET_F_MAC, VIRTIO_NET_F_MRG_RXBUF,
32 };
33 use vm_memory::{ByteValued, GuestAddressSpace, GuestMemoryAtomic};
34 use vm_migration::{Migratable, MigratableError, Pausable, Snapshottable, Transportable};
35 use vmm_sys_util::eventfd::EventFd;
36 
37 const DEFAULT_QUEUE_NUMBER: usize = 2;
38 
39 struct SlaveReqHandler {}
40 impl VhostUserMasterReqHandler for SlaveReqHandler {}
41 
42 /// Control queue
43 // Event available on the control queue.
44 const CTRL_QUEUE_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 1;
45 
46 pub struct NetCtrlEpollHandler {
47     pub mem: GuestMemoryAtomic<GuestMemoryMmap>,
48     pub kill_evt: EventFd,
49     pub pause_evt: EventFd,
50     pub ctrl_q: CtrlQueue,
51     pub queue_evt: EventFd,
52     pub queue: Queue,
53 }
54 
55 impl NetCtrlEpollHandler {
56     pub fn run_ctrl(
57         &mut self,
58         paused: Arc<AtomicBool>,
59         paused_sync: Arc<Barrier>,
60     ) -> std::result::Result<(), EpollHelperError> {
61         let mut helper = EpollHelper::new(&self.kill_evt, &self.pause_evt)?;
62         helper.add_event(self.queue_evt.as_raw_fd(), CTRL_QUEUE_EVENT)?;
63         helper.run(paused, paused_sync, self)?;
64 
65         Ok(())
66     }
67 }
68 
69 impl EpollHelperHandler for NetCtrlEpollHandler {
70     fn handle_event(&mut self, _helper: &mut EpollHelper, event: &epoll::Event) -> bool {
71         let ev_type = event.data as u16;
72         match ev_type {
73             CTRL_QUEUE_EVENT => {
74                 let mem = self.mem.memory();
75                 if let Err(e) = self.queue_evt.read() {
76                     error!("failed to get ctl queue event: {:?}", e);
77                     return true;
78                 }
79                 if let Err(e) = self.ctrl_q.process(&mem, &mut self.queue) {
80                     error!("failed to process ctrl queue: {:?}", e);
81                     return true;
82                 }
83             }
84             _ => {
85                 error!("Unknown event for virtio-net");
86                 return true;
87             }
88         }
89 
90         false
91     }
92 }
93 
94 pub struct Net {
95     common: VirtioCommon,
96     id: String,
97     vhost_user_net: Arc<Mutex<Master>>,
98     config: VirtioNetConfig,
99     guest_memory: Option<GuestMemoryAtomic<GuestMemoryMmap>>,
100     acked_protocol_features: u64,
101     socket_path: String,
102     server: bool,
103     ctrl_queue_epoll_thread: Option<thread::JoinHandle<()>>,
104     epoll_thread: Option<thread::JoinHandle<()>>,
105     seccomp_action: SeccompAction,
106 }
107 
108 impl Net {
109     /// Create a new vhost-user-net device
110     pub fn new(
111         id: String,
112         mac_addr: MacAddr,
113         vu_cfg: VhostUserConfig,
114         server: bool,
115         seccomp_action: SeccompAction,
116     ) -> Result<Net> {
117         let mut num_queues = vu_cfg.num_queues;
118 
119         // Filling device and vring features VMM supports.
120         let mut avail_features = 1 << VIRTIO_NET_F_CSUM
121             | 1 << VIRTIO_NET_F_GUEST_CSUM
122             | 1 << VIRTIO_NET_F_GUEST_TSO4
123             | 1 << VIRTIO_NET_F_GUEST_TSO6
124             | 1 << VIRTIO_NET_F_GUEST_ECN
125             | 1 << VIRTIO_NET_F_GUEST_UFO
126             | 1 << VIRTIO_NET_F_HOST_TSO4
127             | 1 << VIRTIO_NET_F_HOST_TSO6
128             | 1 << VIRTIO_NET_F_HOST_ECN
129             | 1 << VIRTIO_NET_F_HOST_UFO
130             | 1 << VIRTIO_NET_F_MRG_RXBUF
131             | 1 << VIRTIO_NET_F_CTRL_VQ
132             | 1 << VIRTIO_F_RING_EVENT_IDX
133             | 1 << VIRTIO_F_VERSION_1
134             | VhostUserVirtioFeatures::PROTOCOL_FEATURES.bits();
135 
136         let mut config = VirtioNetConfig::default();
137         build_net_config_space(&mut config, mac_addr, num_queues, &mut avail_features);
138 
139         let mut vhost_user_net =
140             connect_vhost_user(server, &vu_cfg.socket, num_queues as u64, false)?;
141 
142         let avail_protocol_features = VhostUserProtocolFeatures::MQ
143             | VhostUserProtocolFeatures::CONFIGURE_MEM_SLOTS
144             | VhostUserProtocolFeatures::REPLY_ACK
145             | VhostUserProtocolFeatures::INFLIGHT_SHMFD;
146 
147         let (mut acked_features, acked_protocol_features) = negotiate_features_vhost_user(
148             &mut vhost_user_net,
149             avail_features,
150             avail_protocol_features,
151         )?;
152 
153         let backend_num_queues =
154             if acked_protocol_features & VhostUserProtocolFeatures::MQ.bits() != 0 {
155                 vhost_user_net
156                     .get_queue_num()
157                     .map_err(Error::VhostUserGetQueueMaxNum)? as usize
158             } else {
159                 DEFAULT_QUEUE_NUMBER
160             };
161 
162         if num_queues > backend_num_queues {
163             error!("vhost-user-net requested too many queues ({}) since the backend only supports {}\n",
164                 num_queues, backend_num_queues);
165             return Err(Error::BadQueueNum);
166         }
167 
168         // If the control queue feature has been negotiated, let's increase
169         // the number of queues.
170         if acked_features & (1 << VIRTIO_NET_F_CTRL_VQ) != 0 {
171             num_queues += 1;
172         }
173 
174         // Make sure the virtio feature to set the MAC address is exposed to
175         // the guest, even if it hasn't been negotiated with the backend.
176         acked_features |= 1 << VIRTIO_NET_F_MAC;
177 
178         Ok(Net {
179             id,
180             common: VirtioCommon {
181                 device_type: VirtioDeviceType::Net as u32,
182                 queue_sizes: vec![vu_cfg.queue_size; num_queues],
183                 avail_features: acked_features,
184                 acked_features: 0,
185                 paused_sync: Some(Arc::new(Barrier::new(2))),
186                 min_queues: DEFAULT_QUEUE_NUMBER as u16,
187                 ..Default::default()
188             },
189             vhost_user_net: Arc::new(Mutex::new(vhost_user_net)),
190             config,
191             guest_memory: None,
192             acked_protocol_features,
193             socket_path: vu_cfg.socket,
194             server,
195             ctrl_queue_epoll_thread: None,
196             epoll_thread: None,
197             seccomp_action,
198         })
199     }
200 }
201 
202 impl Drop for Net {
203     fn drop(&mut self) {
204         if let Some(kill_evt) = self.common.kill_evt.take() {
205             if let Err(e) = kill_evt.write(1) {
206                 error!("failed to kill vhost-user-net: {:?}", e);
207             }
208         }
209     }
210 }
211 
212 impl VirtioDevice for Net {
213     fn device_type(&self) -> u32 {
214         self.common.device_type
215     }
216 
217     fn queue_max_sizes(&self) -> &[u16] {
218         &self.common.queue_sizes
219     }
220 
221     fn features(&self) -> u64 {
222         self.common.avail_features
223     }
224 
225     fn ack_features(&mut self, value: u64) {
226         self.common.ack_features(value)
227     }
228 
229     fn read_config(&self, offset: u64, data: &mut [u8]) {
230         self.read_config_from_slice(self.config.as_slice(), offset, data);
231     }
232 
233     fn activate(
234         &mut self,
235         mem: GuestMemoryAtomic<GuestMemoryMmap>,
236         interrupt_cb: Arc<dyn VirtioInterrupt>,
237         mut queues: Vec<Queue>,
238         mut queue_evts: Vec<EventFd>,
239     ) -> ActivateResult {
240         self.common.activate(&queues, &queue_evts, &interrupt_cb)?;
241 
242         self.guest_memory = Some(mem.clone());
243 
244         let num_queues = queues.len();
245         if self.common.feature_acked(VIRTIO_NET_F_CTRL_VQ.into()) && num_queues % 2 != 0 {
246             let cvq_queue = queues.remove(num_queues - 1);
247             let cvq_queue_evt = queue_evts.remove(num_queues - 1);
248 
249             let (kill_evt, pause_evt) = self.common.dup_eventfds();
250 
251             let mut ctrl_handler = NetCtrlEpollHandler {
252                 mem: mem.clone(),
253                 kill_evt,
254                 pause_evt,
255                 ctrl_q: CtrlQueue::new(Vec::new()),
256                 queue: cvq_queue,
257                 queue_evt: cvq_queue_evt,
258             };
259 
260             let paused = self.common.paused.clone();
261             // Let's update the barrier as we need 1 for the control queue
262             // thread + 1 for the common vhost-user thread + 1 for the main
263             // thread signalling the pause.
264             self.common.paused_sync = Some(Arc::new(Barrier::new(3)));
265             let paused_sync = self.common.paused_sync.clone();
266 
267             // Retrieve seccomp filter for virtio_net_ctl thread
268             let virtio_vhost_net_ctl_seccomp_filter =
269                 get_seccomp_filter(&self.seccomp_action, Thread::VirtioVhostNetCtl)
270                     .map_err(ActivateError::CreateSeccompFilter)?;
271             thread::Builder::new()
272                 .name(format!("{}_ctrl", self.id))
273                 .spawn(move || {
274                     if let Err(e) = SeccompFilter::apply(virtio_vhost_net_ctl_seccomp_filter) {
275                         error!("Error applying seccomp filter: {:?}", e);
276                     } else if let Err(e) = ctrl_handler.run_ctrl(paused, paused_sync.unwrap()) {
277                         error!("Error running worker: {:?}", e);
278                     }
279                 })
280                 .map(|thread| self.ctrl_queue_epoll_thread = Some(thread))
281                 .map_err(|e| {
282                     error!("failed to clone queue EventFd: {}", e);
283                     ActivateError::BadActivate
284                 })?;
285         }
286 
287         let slave_req_handler: Option<MasterReqHandler<SlaveReqHandler>> = None;
288 
289         // The backend acknowledged features must contain the protocol feature
290         // bit in case it was initially set but lost through the features
291         // negotiation with the guest. Additionally, it must not contain
292         // VIRTIO_NET_F_MAC since we don't expect the backend to handle it.
293         let backend_acked_features = self.common.acked_features & !(1 << VIRTIO_NET_F_MAC)
294             | (self.common.avail_features & VhostUserVirtioFeatures::PROTOCOL_FEATURES.bits());
295 
296         let mut inflight: Option<Inflight> =
297             if self.acked_protocol_features & VhostUserProtocolFeatures::INFLIGHT_SHMFD.bits() != 0
298             {
299                 Some(Inflight::default())
300             } else {
301                 None
302             };
303 
304         setup_vhost_user(
305             &mut self.vhost_user_net.lock().unwrap(),
306             &mem.memory(),
307             queues.clone(),
308             queue_evts.iter().map(|q| q.try_clone().unwrap()).collect(),
309             &interrupt_cb,
310             backend_acked_features,
311             &slave_req_handler,
312             inflight.as_mut(),
313         )
314         .map_err(ActivateError::VhostUserNetSetup)?;
315 
316         // Run a dedicated thread for handling potential reconnections with
317         // the backend.
318         let (kill_evt, pause_evt) = self.common.dup_eventfds();
319 
320         let mut handler: VhostUserEpollHandler<SlaveReqHandler> = VhostUserEpollHandler {
321             vu: self.vhost_user_net.clone(),
322             mem,
323             kill_evt,
324             pause_evt,
325             queues,
326             queue_evts,
327             virtio_interrupt: interrupt_cb,
328             acked_features: backend_acked_features,
329             acked_protocol_features: self.acked_protocol_features,
330             socket_path: self.socket_path.clone(),
331             server: self.server,
332             slave_req_handler: None,
333             inflight,
334         };
335 
336         let paused = self.common.paused.clone();
337         let paused_sync = self.common.paused_sync.clone();
338 
339         thread::Builder::new()
340             .name(self.id.to_string())
341             .spawn(move || {
342                 if let Err(e) = handler.run(paused, paused_sync.unwrap()) {
343                     error!("Error running vhost-user-net worker: {:?}", e);
344                 }
345             })
346             .map(|thread| self.epoll_thread = Some(thread))
347             .map_err(|e| {
348                 error!("failed to clone queue EventFd: {}", e);
349                 ActivateError::BadActivate
350             })?;
351 
352         Ok(())
353     }
354 
355     fn reset(&mut self) -> Option<Arc<dyn VirtioInterrupt>> {
356         // We first must resume the virtio thread if it was paused.
357         if self.common.pause_evt.take().is_some() {
358             self.common.resume().ok()?;
359         }
360 
361         if let Err(e) = reset_vhost_user(
362             &mut self.vhost_user_net.lock().unwrap(),
363             self.common.queue_sizes.len(),
364         ) {
365             error!("Failed to reset vhost-user daemon: {:?}", e);
366             return None;
367         }
368 
369         if let Some(kill_evt) = self.common.kill_evt.take() {
370             // Ignore the result because there is nothing we can do about it.
371             let _ = kill_evt.write(1);
372         }
373 
374         event!("virtio-device", "reset", "id", &self.id);
375 
376         // Return the interrupt
377         Some(self.common.interrupt_cb.take().unwrap())
378     }
379 
380     fn shutdown(&mut self) {
381         let _ = unsafe { libc::close(self.vhost_user_net.lock().unwrap().as_raw_fd()) };
382 
383         // Remove socket path if needed
384         if self.server {
385             let _ = std::fs::remove_file(&self.socket_path);
386         }
387     }
388 
389     fn add_memory_region(
390         &mut self,
391         region: &Arc<GuestRegionMmap>,
392     ) -> std::result::Result<(), crate::Error> {
393         if self.acked_protocol_features & VhostUserProtocolFeatures::CONFIGURE_MEM_SLOTS.bits() != 0
394         {
395             add_memory_region(&mut self.vhost_user_net.lock().unwrap(), region)
396                 .map_err(crate::Error::VhostUserAddMemoryRegion)
397         } else if let Some(guest_memory) = &self.guest_memory {
398             update_mem_table(
399                 &mut self.vhost_user_net.lock().unwrap(),
400                 guest_memory.memory().deref(),
401             )
402             .map_err(crate::Error::VhostUserUpdateMemory)
403         } else {
404             Ok(())
405         }
406     }
407 }
408 
409 impl Pausable for Net {
410     fn pause(&mut self) -> result::Result<(), MigratableError> {
411         self.common.pause()
412     }
413 
414     fn resume(&mut self) -> result::Result<(), MigratableError> {
415         self.common.resume()?;
416 
417         if let Some(ctrl_queue_epoll_thread) = &self.ctrl_queue_epoll_thread {
418             ctrl_queue_epoll_thread.thread().unpark();
419         }
420 
421         if let Some(epoll_thread) = &self.epoll_thread {
422             epoll_thread.thread().unpark();
423         }
424 
425         Ok(())
426     }
427 }
428 
429 impl Snapshottable for Net {
430     fn id(&self) -> String {
431         self.id.clone()
432     }
433 }
434 impl Transportable for Net {}
435 impl Migratable for Net {}
436