1use 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#[sorted]
67#[derive(Error, Debug)]
68pub enum Error {
69 #[error("failed to create file system: {0}")]
71 CreateFs(io::Error),
72 #[error("failed to create WaitContext: {0}")]
74 CreateWaitContext(SysError),
75 #[error("fuse error: {0}")]
77 FuseError(fuse::Error),
78 #[error("failed to get uids for the worker thread: {0}")]
80 GetResuid(SysError),
81 #[error("failed to get securebits for the worker thread: {0}")]
83 GetSecurebits(SysError),
84 #[error("request does not have any readable descriptors")]
86 NoReadableDescriptors,
87 #[error("request does not have any writable descriptors")]
89 NoWritableDescriptors,
90 #[error("failed to read from virtio queue Event: {0}")]
92 ReadQueueEvent(SysError),
93 #[error("failed to set securebits for the worker thread: {0}")]
95 SetSecurebits(SysError),
96 #[error("failed to signal used queue: {0}")]
98 SignalUsedQueue(SysError),
99 #[error("Fs device tag is too long: len = {0}, max = {FS_MAX_TAG_LEN}")]
101 TagTooLong(usize),
102 #[error("failed to unshare fs from parent: {0}")]
104 UnshareFromParent(SysError),
105 #[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 let num_queues = num_workers + 1;
155
156 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 let unrequested_features = v & !self.avail_features;
203 if unrequested_features != 0 {
204 warn!("virtio_fs got unknown feature ack: {:x}", v);
205
206 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 if let Some(pci_bar) = self.pci_bar {
238 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}