pxar: fuse: cache goodbye table for each directory on opendir and release it on releasedir

Cache not only the goodbye table for the last directory but for each opened
directory.
The opendir fuse callback will fill the cache with the goodbye table and
releasedir will remove it from the cache.
This should reduce the number of chuncks fetched from the server in some cases.

Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
This commit is contained in:
Christian Ebner 2019-12-06 13:53:38 +01:00 committed by Dietmar Maurer
parent 02491b8fc6
commit 25ad4cbf63
1 changed files with 53 additions and 62 deletions

View File

@ -86,30 +86,12 @@ struct FuseArgs {
/// offset within the archive for the i-node given by the caller. /// offset within the archive for the i-node given by the caller.
struct Context { struct Context {
decoder: Decoder, decoder: Decoder,
goodbye_cache: Option<(Inode, Vec<(PxarGoodbyeItem, Offset, Offset)>)>, goodbye_cache: HashMap<Inode, Vec<(PxarGoodbyeItem, Offset, Offset)>>,
attr_cache: Option<(Inode, PxarAttributes)>, attr_cache: Option<(Inode, PxarAttributes)>,
ino_offset: Offset, ino_offset: Offset,
} }
impl Context { impl Context {
/// Update the goodbye table to the one corresponding to the i-node offset, both
/// given in the `Context`.
fn update_goodbye_cache(&mut self) -> Result<(), i32> {
if let Some((off, _)) = self.goodbye_cache {
if off == self.ino_offset {
// Cache contains already the correct goodbye table
return Ok(());
}
}
// Inode did not match or cache is empty, need to update the cache
let gbt = self.decoder
.goodbye_table(None, self.ino_offset + GOODBYE_ITEM_SIZE)
.map_err(|_| libc::EIO)?;
self.goodbye_cache = Some((self.ino_offset, gbt));
Ok(())
}
/// Lookup the goodbye item identified by `filename` and its corresponding `hash` /// Lookup the goodbye item identified by `filename` and its corresponding `hash`
/// ///
/// Updates the goodbye table cache to contain the table for the directory given /// Updates the goodbye table cache to contain the table for the directory given
@ -129,8 +111,8 @@ impl Context {
filename: &CStr, filename: &CStr,
hash: u64, hash: u64,
) -> Result<(u64, PxarEntry, PxarAttributes, u64), i32> { ) -> Result<(u64, PxarEntry, PxarAttributes, u64), i32> {
self.update_goodbye_cache()?; let gbt = self.goodbye_cache.get(&self.ino_offset)
if let Some((_, gbt)) = &self.goodbye_cache { .ok_or_else(|| libc::EIO)?;
let mut start_idx = 0; let mut start_idx = 0;
let mut skip_multiple = 0; let mut skip_multiple = 0;
loop { loop {
@ -164,9 +146,6 @@ impl Context {
skip_multiple = 1; skip_multiple = 1;
} }
} }
Err(libc::ENOENT)
}
} }
/// `Session` stores a pointer to the session context and is used to mount the /// `Session` stores a pointer to the session context and is used to mount the
@ -252,16 +231,21 @@ impl Session {
/// Options have to be provided as comma separated OsStr, e.g. /// Options have to be provided as comma separated OsStr, e.g.
/// ("ro,default_permissions"). /// ("ro,default_permissions").
pub fn new( pub fn new(
decoder: Decoder, mut decoder: Decoder,
options: &OsStr, options: &OsStr,
verbose: bool, verbose: bool,
) -> Result<Self, Error> { ) -> Result<Self, Error> {
let args = Self::setup_args(options, verbose)?; let args = Self::setup_args(options, verbose)?;
let oprs = Self::setup_callbacks(); let oprs = Self::setup_callbacks();
let root_ino_offset = decoder.root_end_offset() - GOODBYE_ITEM_SIZE;
let root_goodbye_table = decoder.goodbye_table(None, root_ino_offset + GOODBYE_ITEM_SIZE)?;
let mut goodbye_cache = HashMap::new();
goodbye_cache.insert(root_ino_offset, root_goodbye_table);
let ctx = Context { let ctx = Context {
decoder, decoder,
goodbye_cache: None, goodbye_cache,
attr_cache: None, attr_cache: None,
ino_offset: 0, ino_offset: 0,
}; };
@ -323,6 +307,7 @@ impl Session {
oprs.read = Some(Self::read); oprs.read = Some(Self::read);
oprs.opendir = Some(Self::opendir); oprs.opendir = Some(Self::opendir);
oprs.readdir = Some(Self::readdir); oprs.readdir = Some(Self::readdir);
oprs.releasedir = Some(Self::releasedir);
oprs oprs
} }
@ -518,14 +503,12 @@ impl Session {
/// state identifies the directory as opened. /// state identifies the directory as opened.
extern "C" fn opendir(req: Request, inode: u64, fileinfo: MutPtr) { extern "C" fn opendir(req: Request, inode: u64, fileinfo: MutPtr) {
Self::run_in_context(req, inode, |ctx| { Self::run_in_context(req, inode, |ctx| {
let (_, entry, _, _) = ctx let gbt = ctx.decoder
.decoder .goodbye_table(None, ctx.ino_offset + GOODBYE_ITEM_SIZE)
.attributes(ctx.ino_offset)
.map_err(|_| libc::EIO)?; .map_err(|_| libc::EIO)?;
if (entry.mode as u32 & libc::S_IFMT) != libc::S_IFDIR { ctx.goodbye_cache.insert(ctx.ino_offset, gbt);
return Err(libc::ENOENT);
} let _ret = unsafe { fuse_reply_open(req, fileinfo as MutPtr) };
let _ret = unsafe { fuse_reply_open(req, fileinfo) };
Ok(()) Ok(())
}); });
@ -543,8 +526,8 @@ impl Session {
let offset = offset as usize; let offset = offset as usize;
Self::run_in_context(req, inode, |ctx| { Self::run_in_context(req, inode, |ctx| {
ctx.update_goodbye_cache()?; let gb_table = ctx.goodbye_cache.get(&ctx.ino_offset)
let gb_table = &ctx.goodbye_cache.as_ref().unwrap().1; .ok_or_else(|| libc::EIO)?;
let n_entries = gb_table.len(); let n_entries = gb_table.len();
let mut buf = ReplyBuf::new(req, size, offset); let mut buf = ReplyBuf::new(req, size, offset);
@ -603,6 +586,14 @@ impl Session {
buf.reply_filled() buf.reply_filled()
}); });
} }
extern "C" fn releasedir(req: Request, inode: u64, _fileinfo: MutPtr) {
Self::run_in_context(req, inode, |ctx| {
let _gbt = ctx.goodbye_cache.remove(&ctx.ino_offset);
Ok(())
});
}
} }
impl Drop for Session { impl Drop for Session {