• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2019 The ChromiumOS Authors
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::collections::BTreeMap;
6 use std::io;
7 use std::sync::Arc;
8 
9 use anyhow::anyhow;
10 use base::error;
11 use base::warn;
12 use base::AsRawDescriptor;
13 use base::Error as SysError;
14 use base::RawDescriptor;
15 use base::Tube;
16 use base::WorkerThread;
17 use data_model::Le32;
18 use remain::sorted;
19 use resources::Alloc;
20 use sync::Mutex;
21 use thiserror::Error;
22 use virtio_sys::virtio_fs::virtio_fs_config;
23 use virtio_sys::virtio_fs::VIRTIO_FS_SHMCAP_ID_CACHE;
24 use vm_control::FsMappingRequest;
25 use vm_control::VmResponse;
26 use vm_memory::GuestMemory;
27 use zerocopy::AsBytes;
28 
29 use crate::pci::PciAddress;
30 use crate::pci::PciBarConfiguration;
31 use crate::pci::PciBarPrefetchable;
32 use crate::pci::PciBarRegionType;
33 use crate::pci::PciCapability;
34 use crate::virtio::copy_config;
35 use crate::virtio::device_constants::fs::FS_MAX_TAG_LEN;
36 use crate::virtio::DeviceType;
37 use crate::virtio::Interrupt;
38 use crate::virtio::PciCapabilityType;
39 use crate::virtio::Queue;
40 use crate::virtio::VirtioDevice;
41 use crate::virtio::VirtioPciShmCap;
42 
43 mod caps;
44 mod config;
45 mod expiring_map;
46 mod multikey;
47 pub mod passthrough;
48 mod read_dir;
49 mod worker;
50 
51 pub use config::CachePolicy;
52 pub use config::Config;
53 use fuse::Server;
54 use passthrough::PassthroughFs;
55 pub use worker::process_fs_queue;
56 use worker::Worker;
57 
58 const QUEUE_SIZE: u16 = 1024;
59 
60 const FS_BAR_NUM: u8 = 4;
61 const FS_BAR_OFFSET: u64 = 0;
62 const FS_BAR_SIZE: u64 = 1 << 33;
63 
64 /// Errors that may occur during the creation or operation of an Fs device.
65 #[sorted]
66 #[derive(Error, Debug)]
67 pub enum Error {
68     /// Failed to create the file system.
69     #[error("failed to create file system: {0}")]
70     CreateFs(io::Error),
71     /// Creating WaitContext failed.
72     #[error("failed to create WaitContext: {0}")]
73     CreateWaitContext(SysError),
74     /// Error happened in FUSE.
75     #[error("fuse error: {0}")]
76     FuseError(fuse::Error),
77     /// Failed to get the uids for the worker thread.
78     #[error("failed to get uids for the worker thread: {0}")]
79     GetResuid(SysError),
80     /// Failed to get the securebits for the worker thread.
81     #[error("failed to get securebits for the worker thread: {0}")]
82     GetSecurebits(SysError),
83     /// A request is missing readable descriptors.
84     #[error("request does not have any readable descriptors")]
85     NoReadableDescriptors,
86     /// A request is missing writable descriptors.
87     #[error("request does not have any writable descriptors")]
88     NoWritableDescriptors,
89     /// Error while reading from the virtio queue's Event.
90     #[error("failed to read from virtio queue Event: {0}")]
91     ReadQueueEvent(SysError),
92     /// Failed to set the securebits for the worker thread.
93     #[error("failed to set securebits for the worker thread: {0}")]
94     SetSecurebits(SysError),
95     /// Failed to signal the virio used queue.
96     #[error("failed to signal used queue: {0}")]
97     SignalUsedQueue(SysError),
98     /// The tag for the Fs device was too long to fit in the config space.
99     #[error("Fs device tag is too long: len = {0}, max = {}", FS_MAX_TAG_LEN)]
100     TagTooLong(usize),
101     /// Calling unshare to disassociate FS attributes from parent failed.
102     #[error("failed to unshare fs from parent: {0}")]
103     UnshareFromParent(SysError),
104     /// Error while polling for events.
105     #[error("failed to wait for events: {0}")]
106     WaitError(SysError),
107 }
108 
109 impl From<fuse::Error> for Error {
from(err: fuse::Error) -> Error110     fn from(err: fuse::Error) -> Error {
111         Error::FuseError(err)
112     }
113 }
114 
115 pub type Result<T> = ::std::result::Result<T, Error>;
116 
117 pub struct Fs {
118     cfg: virtio_fs_config,
119     tag: String,
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<WorkerThread<Result<()>>>,
127 }
128 
129 impl Fs {
new( base_features: u64, tag: &str, num_workers: usize, fs_cfg: Config, tube: Tube, ) -> Result<Fs>130     pub fn new(
131         base_features: u64,
132         tag: &str,
133         num_workers: usize,
134         fs_cfg: 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(tag, 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             tag: tag.to_string(),
157             fs: Some(fs),
158             queue_sizes: vec![QUEUE_SIZE; num_queues].into_boxed_slice(),
159             avail_features: base_features,
160             acked_features: 0,
161             pci_bar: None,
162             tube: Some(tube),
163             workers: Vec::with_capacity(num_workers + 1),
164         })
165     }
166 }
167 
168 impl VirtioDevice for Fs {
keep_rds(&self) -> Vec<RawDescriptor>169     fn keep_rds(&self) -> Vec<RawDescriptor> {
170         let mut fds = self
171             .fs
172             .as_ref()
173             .map(PassthroughFs::keep_rds)
174             .unwrap_or_default();
175         if let Some(rd) = self.tube.as_ref().map(|s| s.as_raw_descriptor()) {
176             fds.push(rd);
177         }
178 
179         fds
180     }
181 
device_type(&self) -> DeviceType182     fn device_type(&self) -> DeviceType {
183         DeviceType::Fs
184     }
185 
queue_max_sizes(&self) -> &[u16]186     fn queue_max_sizes(&self) -> &[u16] {
187         &self.queue_sizes
188     }
189 
features(&self) -> u64190     fn features(&self) -> u64 {
191         self.avail_features
192     }
193 
ack_features(&mut self, mut v: u64)194     fn ack_features(&mut self, mut v: u64) {
195         // Check if the guest is ACK'ing a feature that we didn't claim to have.
196         let unrequested_features = v & !self.avail_features;
197         if unrequested_features != 0 {
198             warn!("virtio_fs got unknown feature ack: {:x}", v);
199 
200             // Don't count these features as acked.
201             v &= !unrequested_features;
202         }
203         self.acked_features |= v;
204     }
205 
read_config(&self, offset: u64, data: &mut [u8])206     fn read_config(&self, offset: u64, data: &mut [u8]) {
207         copy_config(data, 0, self.cfg.as_bytes(), offset)
208     }
209 
activate( &mut self, _guest_mem: GuestMemory, interrupt: Interrupt, queues: BTreeMap<usize, Queue>, ) -> anyhow::Result<()>210     fn activate(
211         &mut self,
212         _guest_mem: GuestMemory,
213         interrupt: Interrupt,
214         queues: BTreeMap<usize, Queue>,
215     ) -> anyhow::Result<()> {
216         if queues.len() != self.queue_sizes.len() {
217             return Err(anyhow!(
218                 "expected {} queues, got {}",
219                 self.queue_sizes.len(),
220                 queues.len()
221             ));
222         }
223 
224         let fs = self.fs.take().expect("missing file system implementation");
225         let use_dax = fs.cfg().use_dax;
226 
227         let server = Arc::new(Server::new(fs));
228         let socket = self.tube.take().expect("missing mapping socket");
229         let mut slot = 0;
230 
231         // Set up shared memory for DAX.
232         // TODO(b/176129399): Remove cfg! once DAX is supported on ARM.
233         if cfg!(target_arch = "x86_64") && use_dax {
234             // Create the shared memory region now before we start processing requests.
235             let request = FsMappingRequest::AllocateSharedMemoryRegion(
236                 self.pci_bar.as_ref().cloned().expect("No pci_bar"),
237             );
238             socket
239                 .send(&request)
240                 .expect("failed to send allocation message");
241             slot = match socket.recv() {
242                 Ok(VmResponse::RegisterMemory { pfn: _, slot }) => slot,
243                 Ok(VmResponse::Err(e)) => panic!("failed to allocate shared memory region: {}", e),
244                 r => panic!(
245                     "unexpected response to allocate shared memory region: {:?}",
246                     r
247                 ),
248             };
249         }
250 
251         let socket = Arc::new(Mutex::new(socket));
252         let mut watch_resample_event = true;
253 
254         self.workers = queues
255             .into_iter()
256             .map(|(idx, queue)| {
257                 let server = server.clone();
258                 let irq = interrupt.clone();
259                 let socket = Arc::clone(&socket);
260 
261                 let worker =
262                     WorkerThread::start(format!("v_fs:{}:{}", self.tag, idx), move |kill_evt| {
263                         let mut worker = Worker::new(queue, server, irq, socket, slot);
264                         worker.run(kill_evt, watch_resample_event)
265                     });
266 
267                 if watch_resample_event {
268                     watch_resample_event = false;
269                 }
270 
271                 worker
272             })
273             .collect();
274         Ok(())
275     }
276 
get_device_bars(&mut self, address: PciAddress) -> Vec<PciBarConfiguration>277     fn get_device_bars(&mut self, address: PciAddress) -> Vec<PciBarConfiguration> {
278         if self.fs.as_ref().map_or(false, |fs| !fs.cfg().use_dax) {
279             return vec![];
280         }
281 
282         self.pci_bar = Some(Alloc::PciBar {
283             bus: address.bus,
284             dev: address.dev,
285             func: address.func,
286             bar: FS_BAR_NUM,
287         });
288 
289         vec![PciBarConfiguration::new(
290             FS_BAR_NUM as usize,
291             FS_BAR_SIZE,
292             PciBarRegionType::Memory64BitRegion,
293             PciBarPrefetchable::Prefetchable,
294         )]
295     }
296 
get_device_caps(&self) -> Vec<Box<dyn PciCapability>>297     fn get_device_caps(&self) -> Vec<Box<dyn PciCapability>> {
298         if self.fs.as_ref().map_or(false, |fs| !fs.cfg().use_dax) {
299             return vec![];
300         }
301 
302         vec![Box::new(VirtioPciShmCap::new(
303             PciCapabilityType::SharedMemoryConfig,
304             FS_BAR_NUM,
305             FS_BAR_OFFSET,
306             FS_BAR_SIZE,
307             VIRTIO_FS_SHMCAP_ID_CACHE as u8,
308         ))]
309     }
310 }
311