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