• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2019 The Chromium OS Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 use std::io;
6 use std::mem;
7 use std::sync::Arc;
8 use std::thread;
9 
10 use base::{error, warn, AsRawDescriptor, Error as SysError, Event, RawDescriptor, Tube};
11 use data_model::{DataInit, Le32};
12 use remain::sorted;
13 use resources::Alloc;
14 use sync::Mutex;
15 use thiserror::Error;
16 use vm_control::{FsMappingRequest, VmResponse};
17 use vm_memory::GuestMemory;
18 
19 use crate::pci::{
20     PciAddress, PciBarConfiguration, PciBarPrefetchable, PciBarRegionType, PciCapability,
21 };
22 use crate::virtio::{
23     copy_config, DescriptorError, Interrupt, PciCapabilityType, Queue, VirtioDevice,
24     VirtioPciShmCap, TYPE_FS,
25 };
26 
27 mod caps;
28 mod multikey;
29 pub mod passthrough;
30 mod read_dir;
31 mod worker;
32 
33 use fuse::Server;
34 use passthrough::PassthroughFs;
35 use worker::Worker;
36 
37 pub use worker::process_fs_queue;
38 
39 // The fs device does not have a fixed number of queues.
40 pub const QUEUE_SIZE: u16 = 1024;
41 
42 const FS_BAR_NUM: u8 = 4;
43 const FS_BAR_OFFSET: u64 = 0;
44 const FS_BAR_SIZE: u64 = 1 << 33;
45 
46 /// Defined in kernel/include/uapi/linux/virtio_fs.h.
47 const VIRTIO_FS_SHMCAP_ID_CACHE: u8 = 0;
48 
49 /// The maximum allowable length of the tag used to identify a specific virtio-fs device.
50 pub const FS_MAX_TAG_LEN: usize = 36;
51 
52 /// kernel/include/uapi/linux/virtio_fs.h
53 #[repr(C, packed)]
54 #[derive(Clone, Copy)]
55 pub struct virtio_fs_config {
56     /// Filesystem name (UTF-8, not NUL-terminated, padded with NULs)
57     pub tag: [u8; FS_MAX_TAG_LEN],
58     /// Number of request queues
59     pub num_request_queues: Le32,
60 }
61 
62 // Safe because all members are plain old data and any value is valid.
63 unsafe impl DataInit for virtio_fs_config {}
64 
65 /// Errors that may occur during the creation or operation of an Fs device.
66 #[sorted]
67 #[derive(Error, Debug)]
68 pub enum Error {
69     /// Failed to create the file system.
70     #[error("failed to create file system: {0}")]
71     CreateFs(io::Error),
72     /// Creating WaitContext failed.
73     #[error("failed to create WaitContext: {0}")]
74     CreateWaitContext(SysError),
75     /// Error happened in FUSE.
76     #[error("fuse error: {0}")]
77     FuseError(fuse::Error),
78     /// Failed to get the securebits for the worker thread.
79     #[error("failed to get securebits for the worker thread: {0}")]
80     GetSecurebits(SysError),
81     /// The `len` field of the header is too small.
82     #[error("DescriptorChain is invalid: {0}")]
83     InvalidDescriptorChain(DescriptorError),
84     /// A request is missing readable descriptors.
85     #[error("request does not have any readable descriptors")]
86     NoReadableDescriptors,
87     /// A request is missing writable descriptors.
88     #[error("request does not have any writable descriptors")]
89     NoWritableDescriptors,
90     /// Error while reading from the virtio queue's Event.
91     #[error("failed to read from virtio queue Event: {0}")]
92     ReadQueueEvent(SysError),
93     /// Failed to set the securebits for the worker thread.
94     #[error("failed to set securebits for the worker thread: {0}")]
95     SetSecurebits(SysError),
96     /// Failed to signal the virio used queue.
97     #[error("failed to signal used queue: {0}")]
98     SignalUsedQueue(SysError),
99     /// The tag for the Fs device was too long to fit in the config space.
100     #[error("Fs device tag is too long: len = {0}, max = {}", FS_MAX_TAG_LEN)]
101     TagTooLong(usize),
102     /// Calling unshare to disassociate FS attributes from parent failed.
103     #[error("failed to unshare fs from parent: {0}")]
104     UnshareFromParent(SysError),
105     /// Error while polling for events.
106     #[error("failed to wait for events: {0}")]
107     WaitError(SysError),
108 }
109 
110 impl From<fuse::Error> for Error {
from(err: fuse::Error) -> Error111     fn from(err: fuse::Error) -> Error {
112         Error::FuseError(err)
113     }
114 }
115 
116 pub type Result<T> = ::std::result::Result<T, Error>;
117 
118 pub struct Fs {
119     cfg: virtio_fs_config,
120     fs: Option<PassthroughFs>,
121     queue_sizes: Box<[u16]>,
122     avail_features: u64,
123     acked_features: u64,
124     pci_bar: Option<Alloc>,
125     tube: Option<Tube>,
126     workers: Vec<(Event, thread::JoinHandle<Result<()>>)>,
127 }
128 
129 impl Fs {
new( base_features: u64, tag: &str, num_workers: usize, fs_cfg: passthrough::Config, tube: Tube, ) -> Result<Fs>130     pub fn new(
131         base_features: u64,
132         tag: &str,
133         num_workers: usize,
134         fs_cfg: passthrough::Config,
135         tube: Tube,
136     ) -> Result<Fs> {
137         if tag.len() > FS_MAX_TAG_LEN {
138             return Err(Error::TagTooLong(tag.len()));
139         }
140 
141         let mut cfg_tag = [0u8; FS_MAX_TAG_LEN];
142         cfg_tag[..tag.len()].copy_from_slice(tag.as_bytes());
143 
144         let cfg = virtio_fs_config {
145             tag: cfg_tag,
146             num_request_queues: Le32::from(num_workers as u32),
147         };
148 
149         let fs = PassthroughFs::new(fs_cfg).map_err(Error::CreateFs)?;
150 
151         // There is always a high priority queue in addition to the request queues.
152         let num_queues = num_workers + 1;
153 
154         Ok(Fs {
155             cfg,
156             fs: Some(fs),
157             queue_sizes: vec![QUEUE_SIZE; num_queues].into_boxed_slice(),
158             avail_features: base_features,
159             acked_features: 0,
160             pci_bar: None,
161             tube: Some(tube),
162             workers: Vec::with_capacity(num_workers + 1),
163         })
164     }
165 
stop_workers(&mut self)166     fn stop_workers(&mut self) {
167         for (kill_evt, handle) in mem::take(&mut self.workers) {
168             if let Err(e) = kill_evt.write(1) {
169                 error!("failed to kill virtio-fs worker thread: {}", e);
170                 continue;
171             }
172 
173             // Only wait on the child thread if we were able to send it a kill event.
174             match handle.join() {
175                 Ok(r) => {
176                     if let Err(e) = r {
177                         error!("virtio-fs worker thread exited with error: {}", e)
178                     }
179                 }
180                 Err(e) => error!("virtio-fs worker thread panicked: {:?}", e),
181             }
182         }
183     }
184 }
185 
186 impl VirtioDevice for Fs {
keep_rds(&self) -> Vec<RawDescriptor>187     fn keep_rds(&self) -> Vec<RawDescriptor> {
188         let mut fds = self
189             .fs
190             .as_ref()
191             .map(PassthroughFs::keep_rds)
192             .unwrap_or_else(Vec::new);
193         if let Some(rd) = self.tube.as_ref().map(|s| s.as_raw_descriptor()) {
194             fds.push(rd);
195         }
196 
197         fds
198     }
199 
device_type(&self) -> u32200     fn device_type(&self) -> u32 {
201         TYPE_FS
202     }
203 
queue_max_sizes(&self) -> &[u16]204     fn queue_max_sizes(&self) -> &[u16] {
205         &self.queue_sizes
206     }
207 
features(&self) -> u64208     fn features(&self) -> u64 {
209         self.avail_features
210     }
211 
ack_features(&mut self, mut v: u64)212     fn ack_features(&mut self, mut v: u64) {
213         // Check if the guest is ACK'ing a feature that we didn't claim to have.
214         let unrequested_features = v & !self.avail_features;
215         if unrequested_features != 0 {
216             warn!("virtio_fs got unknown feature ack: {:x}", v);
217 
218             // Don't count these features as acked.
219             v &= !unrequested_features;
220         }
221         self.acked_features |= v;
222     }
223 
read_config(&self, offset: u64, data: &mut [u8])224     fn read_config(&self, offset: u64, data: &mut [u8]) {
225         copy_config(data, 0, self.cfg.as_slice(), offset)
226     }
227 
activate( &mut self, guest_mem: GuestMemory, interrupt: Interrupt, queues: Vec<Queue>, queue_evts: Vec<Event>, )228     fn activate(
229         &mut self,
230         guest_mem: GuestMemory,
231         interrupt: Interrupt,
232         queues: Vec<Queue>,
233         queue_evts: Vec<Event>,
234     ) {
235         if queues.len() != self.queue_sizes.len() || queue_evts.len() != self.queue_sizes.len() {
236             return;
237         }
238 
239         let fs = self.fs.take().expect("missing file system implementation");
240         let use_dax = fs.cfg().use_dax;
241 
242         let server = Arc::new(Server::new(fs));
243         let irq = Arc::new(interrupt);
244         let socket = self.tube.take().expect("missing mapping socket");
245         let mut slot = 0;
246 
247         // Set up shared memory for DAX.
248         // TODO(b/176129399): Remove cfg! once DAX is supported on ARM.
249         if cfg!(any(target_arch = "x86", target_arch = "x86_64")) && use_dax {
250             // Create the shared memory region now before we start processing requests.
251             let request = FsMappingRequest::AllocateSharedMemoryRegion(
252                 self.pci_bar.as_ref().cloned().expect("No pci_bar"),
253             );
254             socket
255                 .send(&request)
256                 .expect("failed to send allocation message");
257             slot = match socket.recv() {
258                 Ok(VmResponse::RegisterMemory { pfn: _, slot }) => slot,
259                 Ok(VmResponse::Err(e)) => panic!("failed to allocate shared memory region: {}", e),
260                 r => panic!(
261                     "unexpected response to allocate shared memory region: {:?}",
262                     r
263                 ),
264             };
265         }
266 
267         let socket = Arc::new(Mutex::new(socket));
268         let mut watch_resample_event = true;
269         for (idx, (queue, evt)) in queues.into_iter().zip(queue_evts.into_iter()).enumerate() {
270             let (self_kill_evt, kill_evt) = match Event::new().and_then(|e| Ok((e.try_clone()?, e)))
271             {
272                 Ok(v) => v,
273                 Err(e) => {
274                     error!("fs: failed creating kill Event pair: {}", e);
275                     self.stop_workers();
276                     return;
277                 }
278             };
279 
280             let mem = guest_mem.clone();
281             let server = server.clone();
282             let irq = irq.clone();
283             let socket = Arc::clone(&socket);
284 
285             let worker_result = thread::Builder::new()
286                 .name(format!("virtio-fs worker {}", idx))
287                 .spawn(move || {
288                     let mut worker = Worker::new(mem, queue, server, irq, socket, slot);
289                     worker.run(evt, kill_evt, watch_resample_event)
290                 });
291 
292             if watch_resample_event {
293                 watch_resample_event = false;
294             }
295 
296             match worker_result {
297                 Ok(worker) => self.workers.push((self_kill_evt, worker)),
298                 Err(e) => {
299                     error!("fs: failed to spawn virtio_fs worker: {}", e);
300                     self.stop_workers();
301                     return;
302                 }
303             }
304         }
305     }
306 
get_device_bars(&mut self, address: PciAddress) -> Vec<PciBarConfiguration>307     fn get_device_bars(&mut self, address: PciAddress) -> Vec<PciBarConfiguration> {
308         if self.fs.as_ref().map_or(false, |fs| !fs.cfg().use_dax) {
309             return vec![];
310         }
311 
312         self.pci_bar = Some(Alloc::PciBar {
313             bus: address.bus,
314             dev: address.dev,
315             func: address.func,
316             bar: FS_BAR_NUM,
317         });
318 
319         vec![PciBarConfiguration::new(
320             FS_BAR_NUM as usize,
321             FS_BAR_SIZE,
322             PciBarRegionType::Memory64BitRegion,
323             PciBarPrefetchable::NotPrefetchable,
324         )]
325     }
326 
get_device_caps(&self) -> Vec<Box<dyn PciCapability>>327     fn get_device_caps(&self) -> Vec<Box<dyn PciCapability>> {
328         if self.fs.as_ref().map_or(false, |fs| !fs.cfg().use_dax) {
329             return vec![];
330         }
331 
332         vec![Box::new(VirtioPciShmCap::new(
333             PciCapabilityType::SharedMemoryConfig,
334             FS_BAR_NUM,
335             FS_BAR_OFFSET,
336             FS_BAR_SIZE,
337             VIRTIO_FS_SHMCAP_ID_CACHE,
338         ))]
339     }
340 }
341 
342 impl Drop for Fs {
drop(&mut self)343     fn drop(&mut self) {
344         self.stop_workers()
345     }
346 }
347