Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add the ability to clean up the dentry cache can be used to clean up resources when VFS umount. #152

Merged
merged 4 commits into from
Sep 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/api/pseudo_fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,12 @@ impl PseudoFs {
self.inodes.store(Arc::new(hashmap));
}

pub fn get_parent_inode(&self, ino: u64) -> Option<u64> {
zyfjeff marked this conversation as resolved.
Show resolved Hide resolved
let _guard = self.lock.lock();
let inodes = self.inodes.load();
inodes.get(&ino).map(|o| o.parent)
}

#[allow(dead_code)]
pub fn evict_inode(&self, ino: u64) {
let _guard = self.lock.lock();
Expand Down
34 changes: 34 additions & 0 deletions src/api/server/sync_io.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,44 @@ use crate::abi::virtio_fs::{RemovemappingIn, RemovemappingOne, SetupmappingIn};
use crate::api::filesystem::{
DirEntry, Entry, FileSystem, GetxattrReply, IoctlData, ListxattrReply,
};
#[cfg(feature = "fusedev")]
use crate::transport::FuseDevWriter;
use crate::transport::{pagesize, FsCacheReqHandler, Reader, Writer};
use crate::{bytes_to_cstr, encode_io_error_kind, BitmapSlice, Error, Result};

impl<F: FileSystem + Sync> Server<F> {
#[cfg(feature = "fusedev")]
/// Use to send notify msg to kernel fuse
pub fn notify_inval_entry<S: BitmapSlice>(
&self,
mut w: FuseDevWriter<'_, S>,
parent: u64,
name: &std::ffi::CStr,
) -> Result<usize> {
let mut buffer_writer = w.split_at(0).map_err(Error::FailedToSplitWriter)?;
let mut entry = NotifyInvalEntryOut::default();
let mut header = OutHeader::default();
let name_with_null = name.to_bytes_with_nul();
header.unique = 0;
header.error = NotifyOpcode::InvalEntry as i32;
header.len = std::mem::size_of::<OutHeader>() as u32
+ std::mem::size_of::<NotifyInvalEntryOut>() as u32
+ name_with_null.len() as u32;
// the namelne don't contains the nul
entry.namelen = (name_with_null.len() - 1) as u32;
entry.parent = parent;

buffer_writer
.write_obj(header)
.map_err(Error::FailedToWrite)?;
buffer_writer
.write_obj(entry)
.map_err(Error::FailedToWrite)?;
buffer_writer
.write(name_with_null)
.map_err(Error::FailedToWrite)?;
buffer_writer.commit(None).map_err(Error::InvalidMessage)
}
/// Main entrance to handle requests from the transport layer.
///
/// It receives Fuse requests from transport layers, parses the request according to Fuse ABI,
Expand Down
28 changes: 24 additions & 4 deletions src/api/vfs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,7 @@ pub struct Vfs {
opts: ArcSwap<VfsOptions>,
initialized: AtomicBool,
lock: Mutex<()>,
remove_pseudo_root: bool,
}

impl Default for Vfs {
Expand All @@ -311,9 +312,15 @@ impl Vfs {
opts: ArcSwap::new(Arc::new(opts)),
lock: Mutex::new(()),
initialized: AtomicBool::new(false),
remove_pseudo_root: false,
}
}

/// mark remove pseudo root inode when umount
pub fn set_remove_pseudo_root(&mut self) {
self.remove_pseudo_root = true;
}

/// For sake of live-upgrade, only after negotiation is done, it's safe to persist
/// state of vfs.
pub fn initialized(&self) -> bool {
Expand Down Expand Up @@ -388,15 +395,21 @@ impl Vfs {
}

/// Umount a backend file system at path
pub fn umount(&self, path: &str) -> VfsResult<()> {
pub fn umount(&self, path: &str) -> VfsResult<(u64, u64)> {
// Serialize mount operations. Do not expect poisoned lock here.
let _guard = self.lock.lock().unwrap();
let inode = self
.root
.path_walk(path)
.map_err(VfsError::PathWalk)?
.ok_or_else(|| VfsError::NotFound(path.to_string()))?;

let parent = self
.root
.get_parent_inode(inode)
.ok_or(VfsError::NotFound(format!(
"{}'s parent inode does not exist",
inode
)))?;
let mut mountpoints = self.mountpoints.load().deref().deref().clone();
let fs_idx = mountpoints
.get(&inode)
Expand All @@ -406,7 +419,14 @@ impl Vfs {
// 1. they can be reused later on
// 2. during live upgrade, it is easier reconstruct pseudofs inodes since
// we do not have to track pseudofs deletions
//self.root.evict_inode(inode);
// In order to make the hot upgrade of virtiofs easy, VFS will save pseudo
// inodes when umount for easy recovery. However, in the fuse scenario, if
// umount does not remove the pseudo inode, it will cause an invalid
// directory to be seen on the host, which is not friendly to users. So add
// this option to control this behavior.
if self.remove_pseudo_root {
zyfjeff marked this conversation as resolved.
Show resolved Hide resolved
self.root.evict_inode(inode);
}
mountpoints.remove(&inode);
self.mountpoints.store(Arc::new(mountpoints));
x.fs_idx
Expand All @@ -423,7 +443,7 @@ impl Vfs {
}
self.superblocks.store(Arc::new(superblocks));

Ok(())
Ok((inode, parent))
}

/// Get the mounted backend file system alongside the path if there's one.
Expand Down
10 changes: 9 additions & 1 deletion src/api/vfs/sync_io.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,15 @@ impl FileSystem for Vfs {
(Right(fs), idata) => fs.forget(ctx, idata.ino(), count),
},
Err(e) => {
error!("vfs::forget: failed to get_real_rootfs {:?}", e);
// When a directory is umount and invalidate entry, a forget request is triggered,
// which cannot be forwarded to the backend file system.
// this situation is reasonable, so it is changed to warn here
warn!(
"vfs::forget: failed to get_real_rootfs {:?}, inode: {:?},
maybe it is possible that the vfs submount was dropped by umount,
which is reasonable.",
e, inode
);
}
}
}
Expand Down
6 changes: 6 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,10 @@ pub enum Error {
InvalidXattrSize((u32, usize)),
/// Invalid message that the server cannot handle properly.
InvalidMessage(io::Error),
/// Failed to write buffer to writer.
FailedToWrite(io::Error),
/// Failed to split a writer.
FailedToSplitWriter(transport::Error),
}

impl error::Error for Error {}
Expand All @@ -95,6 +99,8 @@ impl fmt::Display for Error {
decoded value: size = {size}, value.len() = {len}"
),
InvalidMessage(err) => write!(f, "cannot process fuse message: {err}"),
FailedToWrite(err) => write!(f, "cannot write to buffer: {err}"),
FailedToSplitWriter(err) => write!(f, "cannot split a writer: {err}"),
}
}
}
Expand Down
5 changes: 1 addition & 4 deletions src/transport/fusedev/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,10 +157,7 @@ impl<'a, S: BitmapSlice> FuseDevWriter<'a, S> {
}
};

res.map_err(|e| {
error! {"fail to write to fuse device on commit: {}", e};
io::Error::from_raw_os_error(e as i32)
})
res.map_err(|e| io::Error::from_raw_os_error(e as i32))
}

/// Return number of bytes already written to the internal buffer.
Expand Down
Loading