Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
google
GitHub Repository: google/crosvm
Path: blob/main/devices/src/virtio/fs/mod.rs
5394 views
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::IntoBytes;
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
#[cfg(feature = "arc_quota")]
44
mod arc_ioctl;
45
mod caps;
46
mod config;
47
mod expiring_map;
48
mod multikey;
49
pub mod passthrough;
50
mod read_dir;
51
mod worker;
52
53
pub use config::CachePolicy;
54
pub use config::Config;
55
use fuse::Server;
56
use passthrough::PassthroughFs;
57
pub use worker::Worker;
58
59
const QUEUE_SIZE: u16 = 1024;
60
61
const FS_BAR_NUM: u8 = 4;
62
const FS_BAR_OFFSET: u64 = 0;
63
const 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)]
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 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
110
impl From<fuse::Error> for Error {
111
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
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
131
impl 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
174
impl 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
}
303
304