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