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