xref: /cloud-hypervisor/virtio-devices/src/vhost_user/mod.rs (revision 7d7bfb2034001d4cb15df2ddc56d2d350c8da30f)
1 // Copyright 2019 Intel Corporation. All Rights Reserved.
2 // SPDX-License-Identifier: Apache-2.0
3 
4 use crate::{
5     ActivateError, EpollHelper, EpollHelperError, EpollHelperHandler, GuestMemoryMmap,
6     GuestRegionMmap, VirtioInterrupt, EPOLL_HELPER_EVENT_LAST, VIRTIO_F_IN_ORDER,
7     VIRTIO_F_NOTIFICATION_DATA, VIRTIO_F_ORDER_PLATFORM, VIRTIO_F_RING_EVENT_IDX,
8     VIRTIO_F_RING_INDIRECT_DESC, VIRTIO_F_VERSION_1,
9 };
10 use anyhow::anyhow;
11 use std::io;
12 use std::ops::Deref;
13 use std::os::unix::io::AsRawFd;
14 use std::sync::{atomic::AtomicBool, Arc, Barrier, Mutex};
15 use versionize::Versionize;
16 use vhost::vhost_user::message::{
17     VhostUserInflight, VhostUserProtocolFeatures, VhostUserVirtioFeatures,
18 };
19 use vhost::vhost_user::{MasterReqHandler, VhostUserMasterReqHandler};
20 use vhost::Error as VhostError;
21 use virtio_queue::Error as QueueError;
22 use virtio_queue::Queue;
23 use vm_memory::{
24     mmap::MmapRegionError, Address, Error as MmapError, GuestAddressSpace, GuestMemory,
25     GuestMemoryAtomic,
26 };
27 use vm_migration::{protocol::MemoryRangeTable, MigratableError, Snapshot, VersionMapped};
28 use vmm_sys_util::eventfd::EventFd;
29 use vu_common_ctrl::VhostUserHandle;
30 
31 pub mod blk;
32 pub mod fs;
33 pub mod net;
34 pub mod vu_common_ctrl;
35 
36 pub use self::blk::Blk;
37 pub use self::fs::*;
38 pub use self::net::Net;
39 pub use self::vu_common_ctrl::VhostUserConfig;
40 
41 #[derive(Debug)]
42 pub enum Error {
43     /// Failed accepting connection.
44     AcceptConnection(io::Error),
45     /// Invalid available address.
46     AvailAddress,
47     /// Queue number  is not correct
48     BadQueueNum,
49     /// Failed binding vhost-user socket.
50     BindSocket(io::Error),
51     /// Creating kill eventfd failed.
52     CreateKillEventFd(io::Error),
53     /// Cloning kill eventfd failed.
54     CloneKillEventFd(io::Error),
55     /// Invalid descriptor table address.
56     DescriptorTableAddress,
57     /// Signal used queue failed.
58     FailedSignalingUsedQueue(io::Error),
59     /// Failed to read vhost eventfd.
60     MemoryRegions(MmapError),
61     /// Failed removing socket path
62     RemoveSocketPath(io::Error),
63     /// Failed to create master.
64     VhostUserCreateMaster(VhostError),
65     /// Failed to open vhost device.
66     VhostUserOpen(VhostError),
67     /// Connection to socket failed.
68     VhostUserConnect,
69     /// Get features failed.
70     VhostUserGetFeatures(VhostError),
71     /// Get queue max number failed.
72     VhostUserGetQueueMaxNum(VhostError),
73     /// Get protocol features failed.
74     VhostUserGetProtocolFeatures(VhostError),
75     /// Get vring base failed.
76     VhostUserGetVringBase(VhostError),
77     /// Vhost-user Backend not support vhost-user protocol.
78     VhostUserProtocolNotSupport,
79     /// Set owner failed.
80     VhostUserSetOwner(VhostError),
81     /// Reset owner failed.
82     VhostUserResetOwner(VhostError),
83     /// Set features failed.
84     VhostUserSetFeatures(VhostError),
85     /// Set protocol features failed.
86     VhostUserSetProtocolFeatures(VhostError),
87     /// Set mem table failed.
88     VhostUserSetMemTable(VhostError),
89     /// Set vring num failed.
90     VhostUserSetVringNum(VhostError),
91     /// Set vring addr failed.
92     VhostUserSetVringAddr(VhostError),
93     /// Set vring base failed.
94     VhostUserSetVringBase(VhostError),
95     /// Set vring call failed.
96     VhostUserSetVringCall(VhostError),
97     /// Set vring kick failed.
98     VhostUserSetVringKick(VhostError),
99     /// Set vring enable failed.
100     VhostUserSetVringEnable(VhostError),
101     /// Failed to create vhost eventfd.
102     VhostIrqCreate(io::Error),
103     /// Failed to read vhost eventfd.
104     VhostIrqRead(io::Error),
105     /// Failed to read vhost eventfd.
106     VhostUserMemoryRegion(MmapError),
107     /// Failed to create the master request handler from slave.
108     MasterReqHandlerCreation(vhost::vhost_user::Error),
109     /// Set slave request fd failed.
110     VhostUserSetSlaveRequestFd(vhost::Error),
111     /// Add memory region failed.
112     VhostUserAddMemReg(VhostError),
113     /// Failed getting the configuration.
114     VhostUserGetConfig(VhostError),
115     /// Failed setting the configuration.
116     VhostUserSetConfig(VhostError),
117     /// Failed getting inflight shm log.
118     VhostUserGetInflight(VhostError),
119     /// Failed setting inflight shm log.
120     VhostUserSetInflight(VhostError),
121     /// Failed setting the log base.
122     VhostUserSetLogBase(VhostError),
123     /// Invalid used address.
124     UsedAddress,
125     /// Invalid features provided from vhost-user backend
126     InvalidFeatures,
127     /// Missing file descriptor for the region.
128     MissingRegionFd,
129     /// Missing IrqFd
130     MissingIrqFd,
131     /// Failed getting the available index.
132     GetAvailableIndex(QueueError),
133     /// Migration is not supported by this vhost-user device.
134     MigrationNotSupported,
135     /// Failed creating memfd.
136     MemfdCreate(io::Error),
137     /// Failed truncating the file size to the expected size.
138     SetFileSize(io::Error),
139     /// Failed to set the seals on the file.
140     SetSeals(io::Error),
141     /// Failed creating new mmap region
142     NewMmapRegion(MmapRegionError),
143     /// Could not find the shm log region
144     MissingShmLogRegion,
145 }
146 type Result<T> = std::result::Result<T, Error>;
147 
148 pub const DEFAULT_VIRTIO_FEATURES: u64 = 1 << VIRTIO_F_RING_INDIRECT_DESC
149     | 1 << VIRTIO_F_RING_EVENT_IDX
150     | 1 << VIRTIO_F_VERSION_1
151     | 1 << VIRTIO_F_IN_ORDER
152     | 1 << VIRTIO_F_ORDER_PLATFORM
153     | 1 << VIRTIO_F_NOTIFICATION_DATA
154     | VhostUserVirtioFeatures::PROTOCOL_FEATURES.bits();
155 
156 const HUP_CONNECTION_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 1;
157 const SLAVE_REQ_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 2;
158 
159 #[derive(Default)]
160 pub struct Inflight {
161     pub info: VhostUserInflight,
162     pub fd: Option<std::fs::File>,
163 }
164 
165 pub struct VhostUserEpollHandler<S: VhostUserMasterReqHandler> {
166     pub vu: Arc<Mutex<VhostUserHandle>>,
167     pub mem: GuestMemoryAtomic<GuestMemoryMmap>,
168     pub kill_evt: EventFd,
169     pub pause_evt: EventFd,
170     pub queues: Vec<Queue<GuestMemoryAtomic<GuestMemoryMmap>>>,
171     pub queue_evts: Vec<EventFd>,
172     pub virtio_interrupt: Arc<dyn VirtioInterrupt>,
173     pub acked_features: u64,
174     pub acked_protocol_features: u64,
175     pub socket_path: String,
176     pub server: bool,
177     pub slave_req_handler: Option<MasterReqHandler<S>>,
178     pub inflight: Option<Inflight>,
179 }
180 
181 impl<S: VhostUserMasterReqHandler> VhostUserEpollHandler<S> {
182     pub fn run(
183         &mut self,
184         paused: Arc<AtomicBool>,
185         paused_sync: Arc<Barrier>,
186     ) -> std::result::Result<(), EpollHelperError> {
187         let mut helper = EpollHelper::new(&self.kill_evt, &self.pause_evt)?;
188         helper.add_event_custom(
189             self.vu.lock().unwrap().socket_handle().as_raw_fd(),
190             HUP_CONNECTION_EVENT,
191             epoll::Events::EPOLLHUP,
192         )?;
193 
194         if let Some(slave_req_handler) = &self.slave_req_handler {
195             helper.add_event(slave_req_handler.as_raw_fd(), SLAVE_REQ_EVENT)?;
196         }
197 
198         helper.run(paused, paused_sync, self)?;
199 
200         Ok(())
201     }
202 
203     fn reconnect(&mut self, helper: &mut EpollHelper) -> std::result::Result<(), EpollHelperError> {
204         helper.del_event_custom(
205             self.vu.lock().unwrap().socket_handle().as_raw_fd(),
206             HUP_CONNECTION_EVENT,
207             epoll::Events::EPOLLHUP,
208         )?;
209 
210         let mut vhost_user = VhostUserHandle::connect_vhost_user(
211             self.server,
212             &self.socket_path,
213             self.queues.len() as u64,
214             true,
215         )
216         .map_err(|e| {
217             EpollHelperError::IoError(std::io::Error::new(
218                 std::io::ErrorKind::Other,
219                 format!("failed connecting vhost-user backend{:?}", e),
220             ))
221         })?;
222 
223         // Initialize the backend
224         vhost_user
225             .reinitialize_vhost_user(
226                 self.mem.memory().deref(),
227                 self.queues.clone(),
228                 self.queue_evts
229                     .iter()
230                     .map(|q| q.try_clone().unwrap())
231                     .collect(),
232                 &self.virtio_interrupt,
233                 self.acked_features,
234                 self.acked_protocol_features,
235                 &self.slave_req_handler,
236                 self.inflight.as_mut(),
237             )
238             .map_err(|e| {
239                 EpollHelperError::IoError(std::io::Error::new(
240                     std::io::ErrorKind::Other,
241                     format!("failed reconnecting vhost-user backend{:?}", e),
242                 ))
243             })?;
244 
245         helper.add_event_custom(
246             vhost_user.socket_handle().as_raw_fd(),
247             HUP_CONNECTION_EVENT,
248             epoll::Events::EPOLLHUP,
249         )?;
250 
251         // Update vhost-user reference
252         let mut vu = self.vu.lock().unwrap();
253         *vu = vhost_user;
254 
255         Ok(())
256     }
257 }
258 
259 impl<S: VhostUserMasterReqHandler> EpollHelperHandler for VhostUserEpollHandler<S> {
260     fn handle_event(&mut self, helper: &mut EpollHelper, event: &epoll::Event) -> bool {
261         let ev_type = event.data as u16;
262         match ev_type {
263             HUP_CONNECTION_EVENT => {
264                 if let Err(e) = self.reconnect(helper) {
265                     error!("failed to reconnect vhost-user backend: {:?}", e);
266                     return true;
267                 }
268             }
269             SLAVE_REQ_EVENT => {
270                 if let Some(slave_req_handler) = self.slave_req_handler.as_mut() {
271                     if let Err(e) = slave_req_handler.handle_request() {
272                         error!("Failed to handle request from vhost-user backend: {:?}", e);
273                         return true;
274                     }
275                 }
276             }
277             _ => {
278                 error!("Unknown event for vhost-user thread");
279                 return true;
280             }
281         }
282 
283         false
284     }
285 }
286 
287 #[derive(Default)]
288 pub struct VhostUserCommon {
289     pub vu: Option<Arc<Mutex<VhostUserHandle>>>,
290     pub acked_protocol_features: u64,
291     pub socket_path: String,
292     pub vu_num_queues: usize,
293     pub migration_started: bool,
294     pub server: bool,
295 }
296 
297 impl VhostUserCommon {
298     #[allow(clippy::too_many_arguments)]
299     pub fn activate<T: VhostUserMasterReqHandler>(
300         &mut self,
301         mem: GuestMemoryAtomic<GuestMemoryMmap>,
302         queues: Vec<Queue<GuestMemoryAtomic<GuestMemoryMmap>>>,
303         queue_evts: Vec<EventFd>,
304         interrupt_cb: Arc<dyn VirtioInterrupt>,
305         acked_features: u64,
306         slave_req_handler: Option<MasterReqHandler<T>>,
307         kill_evt: EventFd,
308         pause_evt: EventFd,
309     ) -> std::result::Result<VhostUserEpollHandler<T>, ActivateError> {
310         let mut inflight: Option<Inflight> =
311             if self.acked_protocol_features & VhostUserProtocolFeatures::INFLIGHT_SHMFD.bits() != 0
312             {
313                 Some(Inflight::default())
314             } else {
315                 None
316             };
317 
318         if self.vu.is_none() {
319             error!("Missing vhost-user handle");
320             return Err(ActivateError::BadActivate);
321         }
322         let vu = self.vu.as_ref().unwrap();
323         vu.lock()
324             .unwrap()
325             .setup_vhost_user(
326                 &mem.memory(),
327                 queues.clone(),
328                 queue_evts.iter().map(|q| q.try_clone().unwrap()).collect(),
329                 &interrupt_cb,
330                 acked_features,
331                 &slave_req_handler,
332                 inflight.as_mut(),
333             )
334             .map_err(ActivateError::VhostUserBlkSetup)?;
335 
336         Ok(VhostUserEpollHandler {
337             vu: vu.clone(),
338             mem,
339             kill_evt,
340             pause_evt,
341             queues,
342             queue_evts,
343             virtio_interrupt: interrupt_cb,
344             acked_features,
345             acked_protocol_features: self.acked_protocol_features,
346             socket_path: self.socket_path.clone(),
347             server: self.server,
348             slave_req_handler,
349             inflight,
350         })
351     }
352 
353     pub fn restore_backend_connection(&mut self, acked_features: u64) -> Result<()> {
354         let mut vu = VhostUserHandle::connect_vhost_user(
355             self.server,
356             &self.socket_path,
357             self.vu_num_queues as u64,
358             false,
359         )?;
360 
361         vu.set_protocol_features_vhost_user(acked_features, self.acked_protocol_features)?;
362 
363         self.vu = Some(Arc::new(Mutex::new(vu)));
364 
365         Ok(())
366     }
367 
368     pub fn shutdown(&mut self) {
369         if let Some(vu) = &self.vu {
370             let _ = unsafe { libc::close(vu.lock().unwrap().socket_handle().as_raw_fd()) };
371         }
372 
373         // Remove socket path if needed
374         if self.server {
375             let _ = std::fs::remove_file(&self.socket_path);
376         }
377     }
378 
379     pub fn add_memory_region(
380         &mut self,
381         guest_memory: &Option<GuestMemoryAtomic<GuestMemoryMmap>>,
382         region: &Arc<GuestRegionMmap>,
383     ) -> std::result::Result<(), crate::Error> {
384         if let Some(vu) = &self.vu {
385             if self.acked_protocol_features & VhostUserProtocolFeatures::CONFIGURE_MEM_SLOTS.bits()
386                 != 0
387             {
388                 return vu
389                     .lock()
390                     .unwrap()
391                     .add_memory_region(region)
392                     .map_err(crate::Error::VhostUserAddMemoryRegion);
393             } else if let Some(guest_memory) = guest_memory {
394                 return vu
395                     .lock()
396                     .unwrap()
397                     .update_mem_table(guest_memory.memory().deref())
398                     .map_err(crate::Error::VhostUserUpdateMemory);
399             }
400         }
401         Ok(())
402     }
403 
404     pub fn pause(&mut self) -> std::result::Result<(), MigratableError> {
405         if let Some(vu) = &self.vu {
406             vu.lock()
407                 .unwrap()
408                 .pause_vhost_user(self.vu_num_queues)
409                 .map_err(|e| {
410                     MigratableError::Pause(anyhow!("Error pausing vhost-user-blk backend: {:?}", e))
411                 })
412         } else {
413             Ok(())
414         }
415     }
416 
417     pub fn resume(&mut self) -> std::result::Result<(), MigratableError> {
418         if let Some(vu) = &self.vu {
419             vu.lock()
420                 .unwrap()
421                 .resume_vhost_user(self.vu_num_queues)
422                 .map_err(|e| {
423                     MigratableError::Resume(anyhow!(
424                         "Error resuming vhost-user-blk backend: {:?}",
425                         e
426                     ))
427                 })
428         } else {
429             Ok(())
430         }
431     }
432 
433     pub fn snapshot<T>(
434         &mut self,
435         id: &str,
436         state: &T,
437     ) -> std::result::Result<Snapshot, MigratableError>
438     where
439         T: Versionize + VersionMapped,
440     {
441         let snapshot = Snapshot::new_from_versioned_state(id, state)?;
442 
443         if self.migration_started {
444             self.shutdown();
445         }
446 
447         Ok(snapshot)
448     }
449 
450     pub fn start_dirty_log(
451         &mut self,
452         guest_memory: &Option<GuestMemoryAtomic<GuestMemoryMmap>>,
453     ) -> std::result::Result<(), MigratableError> {
454         if let Some(vu) = &self.vu {
455             if let Some(guest_memory) = guest_memory {
456                 let last_ram_addr = guest_memory.memory().last_addr().raw_value();
457                 vu.lock()
458                     .unwrap()
459                     .start_dirty_log(last_ram_addr)
460                     .map_err(|e| {
461                         MigratableError::StartDirtyLog(anyhow!(
462                             "Error starting migration for vhost-user backend: {:?}",
463                             e
464                         ))
465                     })
466             } else {
467                 Err(MigratableError::StartDirtyLog(anyhow!(
468                     "Missing guest memory"
469                 )))
470             }
471         } else {
472             Ok(())
473         }
474     }
475 
476     pub fn stop_dirty_log(&mut self) -> std::result::Result<(), MigratableError> {
477         if let Some(vu) = &self.vu {
478             vu.lock().unwrap().stop_dirty_log().map_err(|e| {
479                 MigratableError::StopDirtyLog(anyhow!(
480                     "Error stopping migration for vhost-user backend: {:?}",
481                     e
482                 ))
483             })
484         } else {
485             Ok(())
486         }
487     }
488 
489     pub fn dirty_log(
490         &mut self,
491         guest_memory: &Option<GuestMemoryAtomic<GuestMemoryMmap>>,
492     ) -> std::result::Result<MemoryRangeTable, MigratableError> {
493         if let Some(vu) = &self.vu {
494             if let Some(guest_memory) = guest_memory {
495                 let last_ram_addr = guest_memory.memory().last_addr().raw_value();
496                 vu.lock().unwrap().dirty_log(last_ram_addr).map_err(|e| {
497                     MigratableError::DirtyLog(anyhow!(
498                         "Error retrieving dirty ranges from vhost-user backend: {:?}",
499                         e
500                     ))
501                 })
502             } else {
503                 Err(MigratableError::DirtyLog(anyhow!("Missing guest memory")))
504             }
505         } else {
506             Ok(MemoryRangeTable::default())
507         }
508     }
509 
510     pub fn start_migration(&mut self) -> std::result::Result<(), MigratableError> {
511         self.migration_started = true;
512         Ok(())
513     }
514 
515     pub fn complete_migration(
516         &mut self,
517         kill_evt: Option<EventFd>,
518     ) -> std::result::Result<(), MigratableError> {
519         self.migration_started = false;
520 
521         // Make sure the device thread is killed in order to prevent from
522         // reconnections to the socket.
523         if let Some(kill_evt) = kill_evt {
524             kill_evt.write(1).map_err(|e| {
525                 MigratableError::CompleteMigration(anyhow!(
526                     "Error killing vhost-user thread: {:?}",
527                     e
528                 ))
529             })?;
530         }
531 
532         // Drop the vhost-user handler to avoid further calls to fail because
533         // the connection with the backend has been closed.
534         self.vu = None;
535 
536         Ok(())
537     }
538 }
539