Skip to content

Commit

Permalink
virtio/fs/macos: implement DAX support
Browse files Browse the repository at this point in the history
Bring virtiofs DAX support into macOS relying on the mechanisms added
for supporting virtio-gpu blobs, which allow us to request HVF the
injection of memory regions.

Signed-off-by: Sergio Lopez <[email protected]>
  • Loading branch information
slp committed Aug 9, 2024
1 parent 8455b62 commit c901b20
Show file tree
Hide file tree
Showing 6 changed files with 227 additions and 9 deletions.
15 changes: 15 additions & 0 deletions src/devices/src/virtio/fs/device.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
#[cfg(target_os = "macos")]
use crossbeam_channel::Sender;
use std::cmp;
use std::io::Write;
use std::sync::atomic::AtomicUsize;
use std::sync::{Arc, Mutex};
use std::thread::JoinHandle;

#[cfg(target_os = "macos")]
use hvf::MemoryMapping;
use utils::eventfd::{EventFd, EFD_NONBLOCK};
use virtio_bindings::{virtio_config::VIRTIO_F_VERSION_1, virtio_ring::VIRTIO_RING_F_EVENT_IDX};
use vm_memory::{ByteValued, GuestMemoryMmap};
Expand Down Expand Up @@ -49,6 +53,8 @@ pub struct Fs {
passthrough_cfg: passthrough::Config,
worker_thread: Option<JoinHandle<()>>,
worker_stopfd: EventFd,
#[cfg(target_os = "macos")]
map_sender: Option<Sender<MemoryMapping>>,
}

impl Fs {
Expand Down Expand Up @@ -90,6 +96,8 @@ impl Fs {
passthrough_cfg: fs_cfg,
worker_thread: None,
worker_stopfd: EventFd::new(EFD_NONBLOCK).map_err(FsError::EventFd)?,
#[cfg(target_os = "macos")]
map_sender: None,
})
}

Expand All @@ -112,6 +120,11 @@ impl Fs {
pub fn set_shm_region(&mut self, shm_region: VirtioShmRegion) {
self.shm_region = Some(shm_region);
}

#[cfg(target_os = "macos")]
pub fn set_map_sender(&mut self, map_sender: Sender<MemoryMapping>) {
self.map_sender = Some(map_sender);
}
}

impl VirtioDevice for Fs {
Expand Down Expand Up @@ -202,6 +215,8 @@ impl VirtioDevice for Fs {
self.shm_region.clone(),
self.passthrough_cfg.clone(),
self.worker_stopfd.try_clone().unwrap(),
#[cfg(target_os = "macos")]
self.map_sender.clone(),
);
self.worker_thread = Some(worker.run());

Expand Down
7 changes: 7 additions & 0 deletions src/devices/src/virtio/fs/filesystem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#[cfg(target_os = "macos")]
use crossbeam_channel::Sender;
#[cfg(target_os = "macos")]
use hvf::MemoryMapping;

use std::convert::TryInto;
use std::ffi::{CStr, CString};
use std::fs::File;
Expand Down Expand Up @@ -1121,6 +1126,7 @@ pub trait FileSystem {
moffset: u64,
host_shm_base: u64,
shm_size: u64,
#[cfg(target_os = "macos")] map_sender: &Option<Sender<MemoryMapping>>,
) -> io::Result<()> {
Err(io::Error::from_raw_os_error(libc::ENOSYS))
}
Expand All @@ -1131,6 +1137,7 @@ pub trait FileSystem {
requests: Vec<RemovemappingOne>,
host_shm_base: u64,
shm_size: u64,
#[cfg(target_os = "macos")] map_sender: &Option<Sender<MemoryMapping>>,
) -> io::Result<()> {
Err(io::Error::from_raw_os_error(libc::ENOSYS))
}
Expand Down
139 changes: 139 additions & 0 deletions src/devices/src/virtio/fs/macos/passthrough.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,22 @@

use std::collections::btree_map;
use std::collections::BTreeMap;
use std::collections::HashMap;
use std::ffi::{CStr, CString};
use std::fs::File;
use std::io;
#[cfg(not(feature = "efi"))]
use std::mem;
use std::mem::MaybeUninit;
use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
use std::ptr::null_mut;
use std::str::FromStr;
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
use std::sync::{Arc, Mutex, RwLock};
use std::time::Duration;

use crossbeam_channel::{unbounded, Sender};
use hvf::MemoryMapping;
use vm_memory::ByteValued;

use crate::virtio::fs::filesystem::SecContext;
Expand Down Expand Up @@ -411,6 +415,8 @@ pub struct PassthroughFs {
next_handle: AtomicU64,
init_handle: u64,

map_windows: Mutex<HashMap<u64, u64>>,

// Whether writeback caching is enabled for this directory. This will only be true when
// `cfg.writeback` is true and `init` was called with `FsOptions::WRITEBACK_CACHE`.
writeback: AtomicBool,
Expand Down Expand Up @@ -446,6 +452,8 @@ impl PassthroughFs {
next_handle: AtomicU64::new(1),
init_handle: 0,

map_windows: Mutex::new(HashMap::new()),

writeback: AtomicBool::new(false),
cfg,
})
Expand Down Expand Up @@ -1802,4 +1810,135 @@ impl FileSystem for PassthroughFs {
Ok(res as u64)
}
}

fn setupmapping(
&self,
_ctx: Context,
inode: Inode,
_handle: Handle,
foffset: u64,
len: u64,
flags: u64,
moffset: u64,
guest_shm_base: u64,
shm_size: u64,
map_sender: &Option<Sender<MemoryMapping>>,
) -> io::Result<()> {
if map_sender.is_none() {
return Err(linux_error(io::Error::from_raw_os_error(libc::ENOSYS)));
}

let prot_flags = if (flags & fuse::SetupmappingFlags::WRITE.bits()) != 0 {
libc::PROT_READ | libc::PROT_WRITE
} else {
libc::PROT_READ
};

if (moffset + len) > shm_size {
return Err(linux_error(io::Error::from_raw_os_error(libc::EINVAL)));
}

let guest_addr = guest_shm_base + moffset;

debug!(
"setupmapping: ino {:?} guest_addr={:x} len={}",
inode, guest_addr, len
);

let file = self.open_inode(inode, libc::O_RDWR)?;
let fd = file.as_raw_fd();

let host_addr = unsafe {
libc::mmap(
null_mut(),
len as usize,
prot_flags,
libc::MAP_SHARED,
fd,
foffset as libc::off_t,
)
};
if host_addr == libc::MAP_FAILED {
return Err(linux_error(io::Error::last_os_error()));
}

let ret = unsafe { libc::close(fd) };
if ret == -1 {
return Err(linux_error(io::Error::last_os_error()));
}

// We've checked that map_sender is something above.
let sender = map_sender.as_ref().unwrap();
let (reply_sender, reply_receiver) = unbounded();
sender
.send(MemoryMapping::AddMapping(
reply_sender,
host_addr as u64,
guest_addr,
len,
))
.unwrap();
if !reply_receiver.recv().unwrap() {
error!("Error requesting HVF the addition of a DAX window");
unsafe { libc::munmap(host_addr, len as usize) };
return Err(linux_error(io::Error::from_raw_os_error(libc::EINVAL)));
}

self.map_windows
.lock()
.unwrap()
.insert(guest_addr, host_addr as u64);

Ok(())
}

fn removemapping(
&self,
_ctx: Context,
requests: Vec<fuse::RemovemappingOne>,
guest_shm_base: u64,
shm_size: u64,
map_sender: &Option<Sender<MemoryMapping>>,
) -> io::Result<()> {
if map_sender.is_none() {
return Err(linux_error(io::Error::from_raw_os_error(libc::ENOSYS)));
}

for req in requests {
let guest_addr = guest_shm_base + req.moffset;
if (req.moffset + req.len) > shm_size {
return Err(linux_error(io::Error::from_raw_os_error(libc::EINVAL)));
}
let host_addr = match self.map_windows.lock().unwrap().remove(&guest_addr) {
Some(a) => a,
None => return Err(linux_error(io::Error::from_raw_os_error(libc::EINVAL))),
};
debug!(
"removemapping: guest_addr={:x} len={:?}",
guest_addr, req.len
);

let sender = map_sender.as_ref().unwrap();
let (reply_sender, reply_receiver) = unbounded();
sender
.send(MemoryMapping::RemoveMapping(
reply_sender,
guest_addr,
req.len,
))
.unwrap();
if !reply_receiver.recv().unwrap() {
error!("Error requesting HVF the removal of a DAX window");
return Err(linux_error(io::Error::from_raw_os_error(libc::EINVAL)));
}

let ret = unsafe { libc::munmap(host_addr as *mut libc::c_void, req.len as usize) };
if ret == -1 {
error!("Error unmapping DAX window");
return Err(linux_error(io::Error::last_os_error()));
}
}

Ok(())
}
}
42 changes: 36 additions & 6 deletions src/devices/src/virtio/fs/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#[cfg(target_os = "macos")]
use crossbeam_channel::Sender;
#[cfg(target_os = "macos")]
use hvf::MemoryMapping;

use std::convert::TryInto;
use std::ffi::{CStr, CString};
use std::fs::File;
Expand Down Expand Up @@ -78,6 +83,7 @@ impl<F: FileSystem + Sync> Server<F> {
mut r: Reader,
w: Writer,
shm_region: &Option<VirtioShmRegion>,
#[cfg(target_os = "macos")] map_sender: &Option<Sender<MemoryMapping>>,
) -> Result<usize> {
let in_header: InHeader = r.read_obj().map_err(Error::DecodeMessage)?;

Expand Down Expand Up @@ -141,15 +147,31 @@ impl<F: FileSystem + Sync> Server<F> {
let shm_base_addr = shm.host_addr;
#[cfg(target_os = "macos")]
let shm_base_addr = shm.guest_addr;
self.setupmapping(in_header, r, w, shm_base_addr, shm.size as u64)
self.setupmapping(
in_header,
r,
w,
shm_base_addr,
shm.size as u64,
#[cfg(target_os = "macos")]
map_sender,
)
}
x if (x == Opcode::RemoveMapping as u32) && shm_region.is_some() => {
let shm = shm_region.as_ref().unwrap();
#[cfg(target_os = "linux")]
let shm_base_addr = shm.host_addr;
#[cfg(target_os = "macos")]
let shm_base_addr = shm.guest_addr;
self.removemapping(in_header, r, w, shm_base_addr, shm.size as u64)
self.removemapping(
in_header,
r,
w,
shm_base_addr,
shm.size as u64,
#[cfg(target_os = "macos")]
map_sender,
)
}
_ => reply_error(
linux_error(io::Error::from_raw_os_error(libc::ENOSYS)),
Expand Down Expand Up @@ -1309,6 +1331,7 @@ impl<F: FileSystem + Sync> Server<F> {
w: Writer,
host_shm_base: u64,
shm_size: u64,
#[cfg(target_os = "macos")] map_sender: &Option<Sender<MemoryMapping>>,
) -> Result<usize> {
let SetupmappingIn {
fh,
Expand All @@ -1328,6 +1351,8 @@ impl<F: FileSystem + Sync> Server<F> {
moffset,
host_shm_base,
shm_size,
#[cfg(target_os = "macos")]
map_sender,
) {
Ok(()) => reply_ok(None::<u8>, None, in_header.unique, w),
Err(e) => reply_error(e, in_header.unique, w),
Expand All @@ -1341,6 +1366,7 @@ impl<F: FileSystem + Sync> Server<F> {
w: Writer,
host_shm_base: u64,
shm_size: u64,
#[cfg(target_os = "macos")] map_sender: &Option<Sender<MemoryMapping>>,
) -> Result<usize> {
let RemovemappingIn { count } = r.read_obj().map_err(Error::DecodeMessage)?;

Expand Down Expand Up @@ -1368,10 +1394,14 @@ impl<F: FileSystem + Sync> Server<F> {
);
}

match self
.fs
.removemapping(Context::from(in_header), requests, host_shm_base, shm_size)
{
match self.fs.removemapping(
Context::from(in_header),
requests,
host_shm_base,
shm_size,
#[cfg(target_os = "macos")]
map_sender,
) {
Ok(()) => reply_ok(None::<u8>, None, in_header.unique, w),
Err(e) => reply_error(e, in_header.unique, w),
}
Expand Down
Loading

0 comments on commit c901b20

Please sign in to comment.