xref: /cloud-hypervisor/virtio-devices/src/vhost_user/fs.rs (revision 5a70d7ec69836ad66cdd1e4ea59414dcdaaeec8c)
1 // Copyright 2019 Intel Corporation. All Rights Reserved.
2 // SPDX-License-Identifier: Apache-2.0
3 
4 use super::vu_common_ctrl::VhostUserHandle;
5 use super::{Error, Result, DEFAULT_VIRTIO_FEATURES};
6 use crate::seccomp_filters::Thread;
7 use crate::thread_helper::spawn_virtio_thread;
8 use crate::vhost_user::VhostUserCommon;
9 use crate::{
10     ActivateResult, UserspaceMapping, VirtioCommon, VirtioDevice, VirtioDeviceType,
11     VirtioInterrupt, VirtioSharedMemoryList, VIRTIO_F_IOMMU_PLATFORM,
12 };
13 use crate::{GuestMemoryMmap, GuestRegionMmap, MmapRegion};
14 use seccompiler::SeccompAction;
15 use serde::{Deserialize, Serialize};
16 use serde_with::{serde_as, Bytes};
17 use std::result;
18 use std::sync::atomic::AtomicBool;
19 use std::sync::{Arc, Barrier, Mutex};
20 use std::thread;
21 use vhost::vhost_user::message::{VhostUserProtocolFeatures, VhostUserVirtioFeatures};
22 use vhost::vhost_user::{FrontendReqHandler, VhostUserFrontend, VhostUserFrontendReqHandler};
23 use virtio_queue::Queue;
24 use vm_memory::{ByteValued, GuestMemoryAtomic};
25 use vm_migration::{
26     protocol::MemoryRangeTable, Migratable, MigratableError, Pausable, Snapshot, Snapshottable,
27     Transportable,
28 };
29 use vmm_sys_util::eventfd::EventFd;
30 
31 const NUM_QUEUE_OFFSET: usize = 1;
32 const DEFAULT_QUEUE_NUMBER: usize = 2;
33 
34 #[derive(Serialize, Deserialize)]
35 pub struct State {
36     pub avail_features: u64,
37     pub acked_features: u64,
38     pub config: VirtioFsConfig,
39     pub acked_protocol_features: u64,
40     pub vu_num_queues: usize,
41     pub backend_req_support: bool,
42 }
43 
44 struct BackendReqHandler {}
45 impl VhostUserFrontendReqHandler for BackendReqHandler {}
46 
47 pub const VIRTIO_FS_TAG_LEN: usize = 36;
48 #[serde_as]
49 #[derive(Copy, Clone, Serialize, Deserialize)]
50 #[repr(C, packed)]
51 pub struct VirtioFsConfig {
52     #[serde_as(as = "Bytes")]
53     pub tag: [u8; VIRTIO_FS_TAG_LEN],
54     pub num_request_queues: u32,
55 }
56 
57 impl Default for VirtioFsConfig {
58     fn default() -> Self {
59         VirtioFsConfig {
60             tag: [0; VIRTIO_FS_TAG_LEN],
61             num_request_queues: 0,
62         }
63     }
64 }
65 
66 // SAFETY: only a series of integers
67 unsafe impl ByteValued for VirtioFsConfig {}
68 
69 pub struct Fs {
70     common: VirtioCommon,
71     vu_common: VhostUserCommon,
72     id: String,
73     config: VirtioFsConfig,
74     // Hold ownership of the memory that is allocated for the device
75     // which will be automatically dropped when the device is dropped
76     cache: Option<(VirtioSharedMemoryList, MmapRegion)>,
77     seccomp_action: SeccompAction,
78     guest_memory: Option<GuestMemoryAtomic<GuestMemoryMmap>>,
79     epoll_thread: Option<thread::JoinHandle<()>>,
80     exit_evt: EventFd,
81     iommu: bool,
82 }
83 
84 impl Fs {
85     /// Create a new virtio-fs device.
86     #[allow(clippy::too_many_arguments)]
87     pub fn new(
88         id: String,
89         path: &str,
90         tag: &str,
91         req_num_queues: usize,
92         queue_size: u16,
93         cache: Option<(VirtioSharedMemoryList, MmapRegion)>,
94         seccomp_action: SeccompAction,
95         exit_evt: EventFd,
96         iommu: bool,
97         state: Option<State>,
98     ) -> Result<Fs> {
99         // Calculate the actual number of queues needed.
100         let num_queues = NUM_QUEUE_OFFSET + req_num_queues;
101 
102         // Connect to the vhost-user socket.
103         let mut vu = VhostUserHandle::connect_vhost_user(false, path, num_queues as u64, false)?;
104 
105         let (
106             avail_features,
107             acked_features,
108             acked_protocol_features,
109             vu_num_queues,
110             config,
111             paused,
112         ) = if let Some(state) = state {
113             info!("Restoring vhost-user-fs {}", id);
114 
115             vu.set_protocol_features_vhost_user(
116                 state.acked_features,
117                 state.acked_protocol_features,
118             )?;
119 
120             (
121                 state.avail_features,
122                 state.acked_features,
123                 state.acked_protocol_features,
124                 state.vu_num_queues,
125                 state.config,
126                 true,
127             )
128         } else {
129             // Filling device and vring features VMM supports.
130             let avail_features = DEFAULT_VIRTIO_FEATURES;
131 
132             let avail_protocol_features = VhostUserProtocolFeatures::MQ
133                 | VhostUserProtocolFeatures::CONFIGURE_MEM_SLOTS
134                 | VhostUserProtocolFeatures::REPLY_ACK
135                 | VhostUserProtocolFeatures::INFLIGHT_SHMFD
136                 | VhostUserProtocolFeatures::LOG_SHMFD;
137 
138             let (acked_features, acked_protocol_features) =
139                 vu.negotiate_features_vhost_user(avail_features, avail_protocol_features)?;
140 
141             let backend_num_queues =
142                 if acked_protocol_features & VhostUserProtocolFeatures::MQ.bits() != 0 {
143                     vu.socket_handle()
144                         .get_queue_num()
145                         .map_err(Error::VhostUserGetQueueMaxNum)? as usize
146                 } else {
147                     DEFAULT_QUEUE_NUMBER
148                 };
149 
150             if num_queues > backend_num_queues {
151                 error!(
152                 "vhost-user-fs requested too many queues ({}) since the backend only supports {}\n",
153                 num_queues, backend_num_queues
154             );
155                 return Err(Error::BadQueueNum);
156             }
157 
158             // Create virtio-fs device configuration.
159             let mut config = VirtioFsConfig::default();
160             let tag_bytes_slice = tag.as_bytes();
161             let len = if tag_bytes_slice.len() < config.tag.len() {
162                 tag_bytes_slice.len()
163             } else {
164                 config.tag.len()
165             };
166             config.tag[..len].copy_from_slice(tag_bytes_slice[..len].as_ref());
167             config.num_request_queues = req_num_queues as u32;
168 
169             (
170                 acked_features,
171                 // If part of the available features that have been acked, the
172                 // PROTOCOL_FEATURES bit must be already set through the VIRTIO
173                 // acked features as we know the guest would never ack it, thus
174                 // the feature would be lost.
175                 acked_features & VhostUserVirtioFeatures::PROTOCOL_FEATURES.bits(),
176                 acked_protocol_features,
177                 num_queues,
178                 config,
179                 false,
180             )
181         };
182 
183         Ok(Fs {
184             common: VirtioCommon {
185                 device_type: VirtioDeviceType::Fs as u32,
186                 avail_features,
187                 acked_features,
188                 queue_sizes: vec![queue_size; num_queues],
189                 paused_sync: Some(Arc::new(Barrier::new(2))),
190                 min_queues: 1,
191                 paused: Arc::new(AtomicBool::new(paused)),
192                 ..Default::default()
193             },
194             vu_common: VhostUserCommon {
195                 vu: Some(Arc::new(Mutex::new(vu))),
196                 acked_protocol_features,
197                 socket_path: path.to_string(),
198                 vu_num_queues,
199                 ..Default::default()
200             },
201             id,
202             config,
203             cache,
204             seccomp_action,
205             guest_memory: None,
206             epoll_thread: None,
207             exit_evt,
208             iommu,
209         })
210     }
211 
212     fn state(&self) -> State {
213         State {
214             avail_features: self.common.avail_features,
215             acked_features: self.common.acked_features,
216             config: self.config,
217             acked_protocol_features: self.vu_common.acked_protocol_features,
218             vu_num_queues: self.vu_common.vu_num_queues,
219             backend_req_support: false,
220         }
221     }
222 }
223 
224 impl Drop for Fs {
225     fn drop(&mut self) {
226         if let Some(kill_evt) = self.common.kill_evt.take() {
227             // Ignore the result because there is nothing we can do about it.
228             let _ = kill_evt.write(1);
229         }
230         self.common.wait_for_epoll_threads();
231         if let Some(thread) = self.epoll_thread.take() {
232             if let Err(e) = thread.join() {
233                 error!("Error joining thread: {:?}", e);
234             }
235         }
236     }
237 }
238 
239 impl VirtioDevice for Fs {
240     fn device_type(&self) -> u32 {
241         self.common.device_type
242     }
243 
244     fn queue_max_sizes(&self) -> &[u16] {
245         &self.common.queue_sizes
246     }
247 
248     fn features(&self) -> u64 {
249         let mut features = self.common.avail_features;
250         if self.iommu {
251             features |= 1u64 << VIRTIO_F_IOMMU_PLATFORM;
252         }
253         features
254     }
255 
256     fn ack_features(&mut self, value: u64) {
257         self.common.ack_features(value)
258     }
259 
260     fn read_config(&self, offset: u64, data: &mut [u8]) {
261         self.read_config_from_slice(self.config.as_slice(), offset, data);
262     }
263 
264     fn activate(
265         &mut self,
266         mem: GuestMemoryAtomic<GuestMemoryMmap>,
267         interrupt_cb: Arc<dyn VirtioInterrupt>,
268         queues: Vec<(usize, Queue, EventFd)>,
269     ) -> ActivateResult {
270         self.common.activate(&queues, &interrupt_cb)?;
271         self.guest_memory = Some(mem.clone());
272 
273         let backend_req_handler: Option<FrontendReqHandler<BackendReqHandler>> = None;
274         // Run a dedicated thread for handling potential reconnections with
275         // the backend.
276         let (kill_evt, pause_evt) = self.common.dup_eventfds();
277 
278         let mut handler = self.vu_common.activate(
279             mem,
280             queues,
281             interrupt_cb,
282             self.common.acked_features,
283             backend_req_handler,
284             kill_evt,
285             pause_evt,
286         )?;
287 
288         let paused = self.common.paused.clone();
289         let paused_sync = self.common.paused_sync.clone();
290 
291         let mut epoll_threads = Vec::new();
292         spawn_virtio_thread(
293             &self.id,
294             &self.seccomp_action,
295             Thread::VirtioVhostFs,
296             &mut epoll_threads,
297             &self.exit_evt,
298             move || handler.run(paused, paused_sync.unwrap()),
299         )?;
300         self.epoll_thread = Some(epoll_threads.remove(0));
301 
302         event!("virtio-device", "activated", "id", &self.id);
303         Ok(())
304     }
305 
306     fn reset(&mut self) -> Option<Arc<dyn VirtioInterrupt>> {
307         // We first must resume the virtio thread if it was paused.
308         if self.common.pause_evt.take().is_some() {
309             self.common.resume().ok()?;
310         }
311 
312         if let Some(vu) = &self.vu_common.vu {
313             if let Err(e) = vu.lock().unwrap().reset_vhost_user() {
314                 error!("Failed to reset vhost-user daemon: {:?}", e);
315                 return None;
316             }
317         }
318 
319         if let Some(kill_evt) = self.common.kill_evt.take() {
320             // Ignore the result because there is nothing we can do about it.
321             let _ = kill_evt.write(1);
322         }
323 
324         event!("virtio-device", "reset", "id", &self.id);
325 
326         // Return the interrupt
327         Some(self.common.interrupt_cb.take().unwrap())
328     }
329 
330     fn shutdown(&mut self) {
331         self.vu_common.shutdown()
332     }
333 
334     fn get_shm_regions(&self) -> Option<VirtioSharedMemoryList> {
335         self.cache.as_ref().map(|cache| cache.0.clone())
336     }
337 
338     fn set_shm_regions(
339         &mut self,
340         shm_regions: VirtioSharedMemoryList,
341     ) -> std::result::Result<(), crate::Error> {
342         if let Some(cache) = self.cache.as_mut() {
343             cache.0 = shm_regions;
344             Ok(())
345         } else {
346             Err(crate::Error::SetShmRegionsNotSupported)
347         }
348     }
349 
350     fn add_memory_region(
351         &mut self,
352         region: &Arc<GuestRegionMmap>,
353     ) -> std::result::Result<(), crate::Error> {
354         self.vu_common.add_memory_region(&self.guest_memory, region)
355     }
356 
357     fn userspace_mappings(&self) -> Vec<UserspaceMapping> {
358         let mut mappings = Vec::new();
359         if let Some(cache) = self.cache.as_ref() {
360             mappings.push(UserspaceMapping {
361                 host_addr: cache.0.host_addr,
362                 mem_slot: cache.0.mem_slot,
363                 addr: cache.0.addr,
364                 len: cache.0.len,
365                 mergeable: false,
366             })
367         }
368 
369         mappings
370     }
371 }
372 
373 impl Pausable for Fs {
374     fn pause(&mut self) -> result::Result<(), MigratableError> {
375         self.vu_common.pause()?;
376         self.common.pause()
377     }
378 
379     fn resume(&mut self) -> result::Result<(), MigratableError> {
380         self.common.resume()?;
381 
382         if let Some(epoll_thread) = &self.epoll_thread {
383             epoll_thread.thread().unpark();
384         }
385 
386         self.vu_common.resume()
387     }
388 }
389 
390 impl Snapshottable for Fs {
391     fn id(&self) -> String {
392         self.id.clone()
393     }
394 
395     fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> {
396         self.vu_common.snapshot(&self.state())
397     }
398 }
399 impl Transportable for Fs {}
400 
401 impl Migratable for Fs {
402     fn start_dirty_log(&mut self) -> std::result::Result<(), MigratableError> {
403         self.vu_common.start_dirty_log(&self.guest_memory)
404     }
405 
406     fn stop_dirty_log(&mut self) -> std::result::Result<(), MigratableError> {
407         self.vu_common.stop_dirty_log()
408     }
409 
410     fn dirty_log(&mut self) -> std::result::Result<MemoryRangeTable, MigratableError> {
411         self.vu_common.dirty_log(&self.guest_memory)
412     }
413 
414     fn start_migration(&mut self) -> std::result::Result<(), MigratableError> {
415         self.vu_common.start_migration()
416     }
417 
418     fn complete_migration(&mut self) -> std::result::Result<(), MigratableError> {
419         self.vu_common
420             .complete_migration(self.common.kill_evt.take())
421     }
422 }
423