devices/virtio/fs/
mod.rs

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
5use std::collections::BTreeMap;
6use std::io;
7use std::sync::Arc;
8
9use anyhow::anyhow;
10use base::error;
11use base::warn;
12use base::AsRawDescriptor;
13use base::Error as SysError;
14use base::RawDescriptor;
15use base::Tube;
16use base::WorkerThread;
17use data_model::Le32;
18use remain::sorted;
19use resources::Alloc;
20use sync::Mutex;
21use thiserror::Error;
22use virtio_sys::virtio_fs::virtio_fs_config;
23use virtio_sys::virtio_fs::VIRTIO_FS_SHMCAP_ID_CACHE;
24use vm_control::FsMappingRequest;
25use vm_control::VmResponse;
26use vm_memory::GuestMemory;
27use zerocopy::IntoBytes;
28
29use crate::pci::PciAddress;
30use crate::pci::PciBarConfiguration;
31use crate::pci::PciBarPrefetchable;
32use crate::pci::PciBarRegionType;
33use crate::pci::PciCapability;
34use crate::virtio::copy_config;
35use crate::virtio::device_constants::fs::FS_MAX_TAG_LEN;
36use crate::virtio::DeviceType;
37use crate::virtio::Interrupt;
38use crate::virtio::PciCapabilityType;
39use crate::virtio::Queue;
40use crate::virtio::VirtioDevice;
41use crate::virtio::VirtioPciShmCap;
42
43#[cfg(feature = "arc_quota")]
44mod arc_ioctl;
45mod caps;
46mod config;
47mod expiring_map;
48mod multikey;
49pub mod passthrough;
50mod read_dir;
51mod worker;
52
53pub use config::CachePolicy;
54pub use config::Config;
55use fuse::Server;
56use passthrough::PassthroughFs;
57pub use worker::Worker;
58
59const QUEUE_SIZE: u16 = 1024;
60
61const FS_BAR_NUM: u8 = 4;
62const FS_BAR_OFFSET: u64 = 0;
63const FS_BAR_SIZE: u64 = 1 << 33;
64
65/// Errors that may occur during the creation or operation of an Fs device.
66#[sorted]
67#[derive(Error, Debug)]
68pub 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 uids for the worker thread.
79    #[error("failed to get uids for the worker thread: {0}")]
80    GetResuid(SysError),
81    /// Failed to get the securebits for the worker thread.
82    #[error("failed to get securebits for the worker thread: {0}")]
83    GetSecurebits(SysError),
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
110impl From<fuse::Error> for Error {
111    fn from(err: fuse::Error) -> Error {
112        Error::FuseError(err)
113    }
114}
115
116pub type Result<T> = ::std::result::Result<T, Error>;
117
118pub struct Fs {
119    cfg: virtio_fs_config,
120    tag: String,
121    fs: Option<PassthroughFs>,
122    queue_sizes: Box<[u16]>,
123    avail_features: u64,
124    acked_features: u64,
125    use_dax: bool,
126    pci_bar: Option<Alloc>,
127    tube: Option<Tube>,
128    workers: Vec<WorkerThread<()>>,
129}
130
131impl Fs {
132    pub fn new(
133        base_features: u64,
134        tag: &str,
135        num_workers: usize,
136        fs_cfg: Config,
137        tube: Tube,
138    ) -> Result<Fs> {
139        if tag.len() > FS_MAX_TAG_LEN {
140            return Err(Error::TagTooLong(tag.len()));
141        }
142
143        let mut cfg_tag = [0u8; FS_MAX_TAG_LEN];
144        cfg_tag[..tag.len()].copy_from_slice(tag.as_bytes());
145
146        let cfg = virtio_fs_config {
147            tag: cfg_tag,
148            num_request_queues: Le32::from(num_workers as u32),
149        };
150
151        let fs = PassthroughFs::new(tag, fs_cfg).map_err(Error::CreateFs)?;
152
153        // There is always a high priority queue in addition to the request queues.
154        let num_queues = num_workers + 1;
155
156        // TODO(b/176129399): Remove cfg! once DAX is supported on ARM.
157        let use_dax = cfg!(target_arch = "x86_64") && fs.cfg().use_dax;
158
159        Ok(Fs {
160            cfg,
161            tag: tag.to_string(),
162            fs: Some(fs),
163            queue_sizes: vec![QUEUE_SIZE; num_queues].into_boxed_slice(),
164            avail_features: base_features,
165            acked_features: 0,
166            use_dax,
167            pci_bar: None,
168            tube: Some(tube),
169            workers: Vec::with_capacity(num_workers + 1),
170        })
171    }
172}
173
174impl VirtioDevice for Fs {
175    fn keep_rds(&self) -> Vec<RawDescriptor> {
176        let mut fds = self
177            .fs
178            .as_ref()
179            .map(PassthroughFs::keep_rds)
180            .unwrap_or_default();
181        if let Some(rd) = self.tube.as_ref().map(|s| s.as_raw_descriptor()) {
182            fds.push(rd);
183        }
184
185        fds
186    }
187
188    fn device_type(&self) -> DeviceType {
189        DeviceType::Fs
190    }
191
192    fn queue_max_sizes(&self) -> &[u16] {
193        &self.queue_sizes
194    }
195
196    fn features(&self) -> u64 {
197        self.avail_features
198    }
199
200    fn ack_features(&mut self, mut v: u64) {
201        // Check if the guest is ACK'ing a feature that we didn't claim to have.
202        let unrequested_features = v & !self.avail_features;
203        if unrequested_features != 0 {
204            warn!("virtio_fs got unknown feature ack: {:x}", v);
205
206            // Don't count these features as acked.
207            v &= !unrequested_features;
208        }
209        self.acked_features |= v;
210    }
211
212    fn read_config(&self, offset: u64, data: &mut [u8]) {
213        copy_config(data, 0, self.cfg.as_bytes(), offset)
214    }
215
216    fn activate(
217        &mut self,
218        _guest_mem: GuestMemory,
219        _interrupt: Interrupt,
220        queues: BTreeMap<usize, Queue>,
221    ) -> anyhow::Result<()> {
222        if queues.len() != self.queue_sizes.len() {
223            return Err(anyhow!(
224                "expected {} queues, got {}",
225                self.queue_sizes.len(),
226                queues.len()
227            ));
228        }
229
230        let fs = self.fs.take().expect("missing file system implementation");
231
232        let server = Arc::new(Server::new(fs));
233        let socket = self.tube.take().expect("missing mapping socket");
234        let mut slot = 0;
235
236        // Set up shared memory for DAX.
237        if let Some(pci_bar) = self.pci_bar {
238            // Create the shared memory region now before we start processing requests.
239            let request = FsMappingRequest::AllocateSharedMemoryRegion(pci_bar);
240            socket
241                .send(&request)
242                .expect("failed to send allocation message");
243            slot = match socket.recv() {
244                Ok(VmResponse::RegisterMemory { slot }) => slot,
245                Ok(VmResponse::Err(e)) => panic!("failed to allocate shared memory region: {e}"),
246                r => panic!("unexpected response to allocate shared memory region: {r:?}"),
247            };
248        }
249
250        let socket = Arc::new(Mutex::new(socket));
251
252        self.workers = queues
253            .into_iter()
254            .map(|(idx, queue)| {
255                let server = server.clone();
256                let socket = Arc::clone(&socket);
257
258                WorkerThread::start(format!("v_fs:{}:{}", self.tag, idx), move |kill_evt| {
259                    let mut worker = Worker::new(queue, server, socket, slot);
260                    if let Err(e) = worker.run(kill_evt) {
261                        error!("virtio-fs worker failed: {e:#}");
262                    }
263                })
264            })
265            .collect();
266        Ok(())
267    }
268
269    fn get_device_bars(&mut self, address: PciAddress) -> Vec<PciBarConfiguration> {
270        if !self.use_dax {
271            return vec![];
272        }
273
274        self.pci_bar = Some(Alloc::PciBar {
275            bus: address.bus,
276            dev: address.dev,
277            func: address.func,
278            bar: FS_BAR_NUM,
279        });
280
281        vec![PciBarConfiguration::new(
282            FS_BAR_NUM as usize,
283            FS_BAR_SIZE,
284            PciBarRegionType::Memory64BitRegion,
285            PciBarPrefetchable::Prefetchable,
286        )]
287    }
288
289    fn get_device_caps(&self) -> Vec<Box<dyn PciCapability>> {
290        if !self.use_dax {
291            return vec![];
292        }
293
294        vec![Box::new(VirtioPciShmCap::new(
295            PciCapabilityType::SharedMemoryConfig,
296            FS_BAR_NUM,
297            FS_BAR_OFFSET,
298            FS_BAR_SIZE,
299            VIRTIO_FS_SHMCAP_ID_CACHE as u8,
300        ))]
301    }
302}