diff --git a/devices/src/virtio/fs/mod.rs b/devices/src/virtio/fs/mod.rs index 452bedde05..c98388e3d6 100644 --- a/devices/src/virtio/fs/mod.rs +++ b/devices/src/virtio/fs/mod.rs @@ -234,6 +234,7 @@ impl VirtioDevice for Fs { } let fs = self.fs.take().expect("missing file system implementation"); + let use_dax = fs.cfg().use_dax; let server = Arc::new(Server::new(fs)); let irq = Arc::new(interrupt); @@ -242,7 +243,7 @@ impl VirtioDevice for Fs { // Set up shared memory for DAX. // TODO(b/176129399): Remove cfg! once DAX is supported on ARM. - if cfg!(any(target_arch = "x86", target_arch = "x86_64")) { + if cfg!(any(target_arch = "x86", target_arch = "x86_64")) && use_dax { // Create the shared memory region now before we start processing requests. let request = FsMappingRequest::AllocateSharedMemoryRegion( self.pci_bar.as_ref().cloned().expect("No pci_bar"), @@ -301,6 +302,10 @@ impl VirtioDevice for Fs { } fn get_device_bars(&mut self, address: PciAddress) -> Vec { + if self.fs.as_ref().map_or(false, |fs| !fs.cfg().use_dax) { + return vec![]; + } + self.pci_bar = Some(Alloc::PciBar { bus: address.bus, dev: address.dev, @@ -317,6 +322,10 @@ impl VirtioDevice for Fs { } fn get_device_caps(&self) -> Vec> { + if self.fs.as_ref().map_or(false, |fs| !fs.cfg().use_dax) { + return vec![]; + } + vec![Box::new(VirtioPciShmCap::new( PciCapabilityType::SharedMemoryConfig, FS_BAR_NUM, diff --git a/devices/src/virtio/fs/passthrough.rs b/devices/src/virtio/fs/passthrough.rs index 08bb20a8f4..eac36d3987 100644 --- a/devices/src/virtio/fs/passthrough.rs +++ b/devices/src/virtio/fs/passthrough.rs @@ -499,6 +499,17 @@ pub struct Config { // CAP_FOWNER. #[cfg(feature = "chromeos")] pub privileged_quota_uids: Vec, + + /// Use DAX for shared files. + /// + /// Enabling DAX can improve performance for frequently accessed files by mapping regions of the + /// file directly into the VM's memory region, allowing direct access with the cost of slightly + /// increased latency the first time the file is accessed. Additionally, since the mapping is + /// shared directly from the host kernel's file cache, enabling DAX can improve performance even + /// when the cache policy is `Never`. + /// + /// The default value for this option is `false`. + pub use_dax: bool, } impl Default for Config { @@ -512,6 +523,7 @@ impl Default for Config { ascii_casefold: false, #[cfg(feature = "chromeos")] privileged_quota_uids: Default::default(), + use_dax: false, } } } @@ -625,6 +637,10 @@ impl PassthroughFs { }) } + pub fn cfg(&self) -> &Config { + &self.cfg + } + pub fn keep_rds(&self) -> Vec { #[cfg_attr(not(feature = "chromeos"), allow(unused_mut))] let mut keep_rds = vec![self.proc.as_raw_descriptor()]; @@ -2481,6 +2497,10 @@ impl FileSystem for PassthroughFs { prot: u32, mapper: M, ) -> io::Result<()> { + if !self.cfg.use_dax { + return Err(io::Error::from_raw_os_error(libc::ENOSYS)); + } + let read = prot & libc::PROT_READ as u32 != 0; let write = prot & libc::PROT_WRITE as u32 != 0; let mmap_flags = match (read, write) { @@ -2522,6 +2542,10 @@ impl FileSystem for PassthroughFs { } fn remove_mapping(&self, msgs: &[RemoveMappingOne], mapper: M) -> io::Result<()> { + if !self.cfg.use_dax { + return Err(io::Error::from_raw_os_error(libc::ENOSYS)); + } + for RemoveMappingOne { moffset, len } in msgs { mapper.unmap(*moffset, *len)?; } diff --git a/src/main.rs b/src/main.rs index e2344004a7..60848cf8df 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1569,6 +1569,13 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument:: shared_dir.fs_cfg.ascii_casefold = ascii_casefold; shared_dir.p9_cfg.ascii_casefold = ascii_casefold; } + "dax" => { + let use_dax = value.parse().map_err(|_| argument::Error::InvalidValue { + value: value.to_owned(), + expected: String::from("`dax` must be a boolean"), + })?; + shared_dir.fs_cfg.use_dax = use_dax; + } _ => { return Err(argument::Error::InvalidValue { value: kind.to_owned(), @@ -2150,7 +2157,7 @@ fn run_vm(args: std::env::Args) -> std::result::Result<(), ()> { "Path to put the control socket. If PATH is a directory, a name will be generated."), Argument::flag("disable-sandbox", "Run all devices in one, non-sandboxed process."), Argument::value("cid", "CID", "Context ID for virtual sockets."), - Argument::value("shared-dir", "PATH:TAG[:type=TYPE:writeback=BOOL:timeout=SECONDS:uidmap=UIDMAP:gidmap=GIDMAP:cache=CACHE]", + Argument::value("shared-dir", "PATH:TAG[:type=TYPE:writeback=BOOL:timeout=SECONDS:uidmap=UIDMAP:gidmap=GIDMAP:cache=CACHE:dax=BOOL]", "Colon-separated options for configuring a directory to be shared with the VM. The first field is the directory to be shared and the second field is the tag that the VM can use to identify the device. The remaining fields are key=value pairs that may appear in any order. Valid keys are: @@ -2160,6 +2167,7 @@ fn run_vm(args: std::env::Args) -> std::result::Result<(), ()> { cache=(never, auto, always) - Indicates whether the VM can cache the contents of the shared directory (default: auto). When set to \"auto\" and the type is \"fs\", the VM will use close-to-open consistency for file contents. timeout=SECONDS - How long the VM should consider file attributes and directory entries to be valid (default: 5). If the VM has exclusive access to the directory, then this should be a large value. If the directory can be modified by other processes, then this should be 0. writeback=BOOL - Indicates whether the VM can use writeback caching (default: false). This is only safe to do when the VM has exclusive access to the files in a directory. Additionally, the server should have read permission for all files as the VM may issue read requests even for files that are opened write-only. + dax=BOOL - Indicates whether DAX support should be enabled. Enabling DAX can improve performance for frequently accessed files by mapping regions of the file directory into the VM's memory, allowing direct access at the cost of slightly increased latency the first time the file is accessed. Since the mapping is shared directly from the host kernel's file cache, enabling DAX can improve performance even when the cache policy is \"Never\". The default value for this option is \"false\". "), Argument::value("seccomp-policy-dir", "PATH", "Path to seccomp .policy files."), Argument::flag("seccomp-log-failures", "Instead of seccomp filter failures being fatal, they will be logged instead."),