From d0bcd2491f67f0d24723aa1aa5fe599ca3ed3ebd Mon Sep 17 00:00:00 2001 From: LI Qing Date: Mon, 27 Mar 2023 01:54:45 -0400 Subject: [PATCH] Rewrite dentry cache --- .../jinux-std/src/fs/utils/dentry_cache.rs | 253 +++++++++++------- 1 file changed, 158 insertions(+), 95 deletions(-) diff --git a/src/services/libs/jinux-std/src/fs/utils/dentry_cache.rs b/src/services/libs/jinux-std/src/fs/utils/dentry_cache.rs index 24d391832..4461d9f37 100644 --- a/src/services/libs/jinux-std/src/fs/utils/dentry_cache.rs +++ b/src/services/libs/jinux-std/src/fs/utils/dentry_cache.rs @@ -1,94 +1,91 @@ use crate::prelude::*; use alloc::string::String; use core::time::Duration; -use spin::RwLockWriteGuard; +use spin::MutexGuard; use super::{InodeMode, InodeType, Metadata, Vnode, NAME_MAX}; +lazy_static! { + static ref DCACHE: Mutex>> = Mutex::new(BTreeMap::new()); +} + +/// The dentry cache to accelerate path lookup pub struct Dentry { - inner: RwLock, vnode: Vnode, -} - -struct Dentry_ { - name: String, + name_and_parent: RwLock<(String, Option>)>, this: Weak, - parent: Option>, - children: BTreeMap>, -} - -impl Dentry_ { - pub fn new(name: &str, parent: Option>) -> Self { - Self { - name: String::from(name), - this: Weak::default(), - parent, - children: BTreeMap::new(), - } - } + children: Mutex, } impl Dentry { /// Create a new dentry cache with root inode pub fn new_root(root_vnode: Vnode) -> Arc { - let root = Self::new("/", root_vnode, None); + let root = Self::new("/", None, root_vnode); + DCACHE.lock().insert(root.key(), root.clone()); root } /// Internal constructor - fn new(name: &str, vnode: Vnode, parent: Option>) -> Arc { - let dentry = { - let inner = RwLock::new(Dentry_::new(name, parent)); - Arc::new(Self { inner, vnode }) - }; - dentry.inner.write().this = Arc::downgrade(&dentry); - dentry + fn new(name: &str, parent: Option>, vnode: Vnode) -> Arc { + Arc::new_cyclic(|weak_self| Self { + vnode, + name_and_parent: RwLock::new((String::from(name), parent)), + this: weak_self.clone(), + children: Mutex::new(Children::new()), + }) } + /// Get the name of Dentry. pub fn name(&self) -> String { - self.inner.read().name.clone() + self.name_and_parent.read().0.clone() } - fn set_name(&self, name: &str) { - self.inner.write().name = String::from(name); + /// Get the parent dentry. + /// + /// Returns None if it is root dentry. + pub fn parent(&self) -> Option> { + self.name_and_parent.read().1.clone() + } + + fn set_name_and_parent(&self, name: &str, parent: Option>) { + let mut name_and_parent = self.name_and_parent.write(); + name_and_parent.0 = String::from(name); + name_and_parent.1 = parent; } fn this(&self) -> Arc { - self.inner.read().this.upgrade().unwrap() + self.this.upgrade().unwrap() } - pub fn parent(&self) -> Option> { - self.inner - .read() - .parent - .as_ref() - .map(|p| p.upgrade().unwrap()) - } - - fn set_parent(&self, parent: &Arc) { - self.inner.write().parent = Some(Arc::downgrade(parent)); + fn key(&self) -> DentryKey { + let parent = self.parent().unwrap_or(self.this()); + DentryKey::new(&self.name_and_parent.read().0, &parent) } pub(in crate::fs) fn vnode(&self) -> &Vnode { &self.vnode } + /// Create a dentry by making inode. pub fn create(&self, name: &str, type_: InodeType, mode: InodeMode) -> Result> { if self.vnode.inode_type() != InodeType::Dir { return_errno!(Errno::ENOTDIR); } - let mut inner = self.inner.write(); - if inner.children.get(name).is_some() { + let mut children = self.children.lock(); + if children.find_dentry(name).is_some() { return_errno!(Errno::EEXIST); } + let child = { let vnode = self.vnode.mknod(name, type_, mode)?; - Dentry::new(name, vnode, Some(inner.this.clone())) + let dentry = Dentry::new(name, Some(self.this()), vnode); + children.insert_dentry(&dentry); + dentry }; - inner.children.insert(String::from(name), child.clone()); Ok(child) } + /// Lookup a dentry. pub fn lookup(&self, name: &str) -> Result> { if self.vnode.inode_type() != InodeType::Dir { return_errno!(Errno::ENOTDIR); @@ -101,55 +98,60 @@ impl Dentry { "." => self.this(), ".." => self.parent().unwrap_or(self.this()), name => { - let mut inner = self.inner.write(); - if let Some(dentry) = inner.children.get(name) { - dentry.clone() - } else { - let vnode = self.vnode.lookup(name)?; - let dentry = Dentry::new(name, vnode, Some(inner.this.clone())); - inner.children.insert(String::from(name), dentry.clone()); - dentry + let mut children = self.children.lock(); + match children.find_dentry(name) { + Some(dentry) => dentry.clone(), + None => { + let vnode = self.vnode.lookup(name)?; + let dentry = Dentry::new(name, Some(self.this()), vnode); + children.insert_dentry(&dentry); + dentry + } } } }; Ok(dentry) } + /// Link a new name for the dentry by linking inode. pub fn link(&self, old: &Arc, name: &str) -> Result<()> { if self.vnode.inode_type() != InodeType::Dir { return_errno!(Errno::ENOTDIR); } - let mut inner = self.inner.write(); - if inner.children.get(name).is_some() { + let mut children = self.children.lock(); + if children.find_dentry(name).is_some() { return_errno!(Errno::EEXIST); } - let target_vnode = old.vnode(); - self.vnode.link(target_vnode, name)?; - let new_dentry = Self::new(name, target_vnode.clone(), Some(inner.this.clone())); - inner.children.insert(String::from(name), new_dentry); + let old_vnode = old.vnode(); + self.vnode.link(old_vnode, name)?; + let dentry = Dentry::new(name, Some(self.this()), old_vnode.clone()); + children.insert_dentry(&dentry); Ok(()) } + /// Delete a dentry by unlinking inode. pub fn unlink(&self, name: &str) -> Result<()> { if self.vnode.inode_type() != InodeType::Dir { return_errno!(Errno::ENOTDIR); } - let mut inner = self.inner.write(); + let mut children = self.children.lock(); self.vnode.unlink(name)?; - inner.children.remove(name); + children.delete_dentry(name); Ok(()) } + /// Delete a directory dentry by rmdiring inode. pub fn rmdir(&self, name: &str) -> Result<()> { if self.vnode.inode_type() != InodeType::Dir { return_errno!(Errno::ENOTDIR); } - let mut inner = self.inner.write(); + let mut children = self.children.lock(); self.vnode.rmdir(name)?; - inner.children.remove(name); + children.delete_dentry(name); Ok(()) } + /// Read symbolic link. pub fn read_link(&self) -> Result { if self.vnode.inode_type() != InodeType::SymLink { return_errno!(Errno::EINVAL); @@ -157,6 +159,7 @@ impl Dentry { self.vnode.read_link() } + /// Write symbolic link. pub fn write_link(&self, target: &str) -> Result<()> { if self.vnode.inode_type() != InodeType::SymLink { return_errno!(Errno::EINVAL); @@ -164,6 +167,7 @@ impl Dentry { self.vnode.write_link(target) } + /// Rename a dentry to the new dentry by renaming inode. pub fn rename(&self, old_name: &str, new_dir: &Arc, new_name: &str) -> Result<()> { if old_name == "." || old_name == ".." || new_name == "." || new_name == ".." { return_errno_with_message!(Errno::EISDIR, "old_name or new_name is a directory"); @@ -178,69 +182,78 @@ impl Dentry { if old_name == new_name { return Ok(()); } - let mut inner = self.inner.write(); - let dentry = if let Some(dentry) = inner.children.get(old_name) { - dentry.clone() - } else { - let vnode = self.vnode.lookup(old_name)?; - Dentry::new(old_name, vnode, Some(inner.this.clone())) - }; + let mut children = self.children.lock(); self.vnode.rename(old_name, &self.vnode, new_name)?; - inner.children.remove(old_name); - dentry.set_name(new_name); - inner.children.insert(String::from(new_name), dentry); + match children.find_dentry(old_name) { + Some(dentry) => { + children.delete_dentry(old_name); + dentry.set_name_and_parent(new_name, Some(self.this())); + children.insert_dentry(&dentry); + } + None => { + children.delete_dentry(new_name); + } + } } else { // Self and new_dir are different Dentry - let (mut self_inner, mut new_dir_inner) = write_lock_two_dentries(&self, &new_dir); - let dentry = if let Some(dentry) = self_inner.children.get(old_name) { - dentry.clone() - } else { - let vnode = self.vnode.lookup(old_name)?; - Dentry::new(old_name, vnode, Some(self_inner.this.clone())) - }; + let (mut self_children, mut new_dir_children) = + write_lock_children_on_two_dentries(&self, &new_dir); self.vnode.rename(old_name, &new_dir.vnode, new_name)?; - self_inner.children.remove(old_name); - dentry.set_name(new_name); - dentry.set_parent(&new_dir.this()); - new_dir_inner - .children - .insert(String::from(new_name), dentry); + match self_children.find_dentry(old_name) { + Some(dentry) => { + self_children.delete_dentry(old_name); + dentry.set_name_and_parent(new_name, Some(new_dir.this())); + new_dir_children.insert_dentry(&dentry); + } + None => { + new_dir_children.delete_dentry(new_name); + } + } } Ok(()) } + /// Get the inode metadata pub fn inode_metadata(&self) -> Metadata { self.vnode.metadata() } + /// Get the inode type pub fn inode_type(&self) -> InodeType { self.vnode.inode_type() } + /// Get the inode permission mode pub fn inode_mode(&self) -> InodeMode { self.vnode.inode_mode() } + /// Get the inode length pub fn inode_len(&self) -> usize { self.vnode.len() } + /// Get the access timestamp pub fn atime(&self) -> Duration { self.vnode.atime() } + /// Set the access timestamp pub fn set_atime(&self, time: Duration) { self.vnode.set_atime(time) } + /// Get the modified timestamp pub fn mtime(&self) -> Duration { self.vnode.mtime() } + /// Set the modified timestamp pub fn set_mtime(&self, time: Duration) { self.vnode.set_mtime(time) } + /// Get the absolute path. pub fn abs_path(&self) -> String { let mut path = self.name(); let mut dentry = self.this(); @@ -267,19 +280,69 @@ impl Dentry { } } -fn write_lock_two_dentries<'a>( +struct Children { + inner: BTreeMap>, +} + +impl Children { + pub fn new() -> Self { + Self { + inner: BTreeMap::new(), + } + } + + pub fn insert_dentry(&mut self, dentry: &Arc) { + DCACHE.lock().insert(dentry.key(), dentry.clone()); + self.inner.insert(dentry.name(), Arc::downgrade(&dentry)); + } + + pub fn delete_dentry(&mut self, name: &str) -> Option> { + self.inner + .remove(name) + .and_then(|d| d.upgrade()) + .and_then(|d| DCACHE.lock().remove(&d.key())) + } + + pub fn find_dentry(&mut self, name: &str) -> Option> { + if let Some(dentry) = self.inner.get(name) { + dentry.upgrade().or_else(|| { + self.inner.remove(name); + None + }) + } else { + None + } + } +} + +#[derive(Clone, Hash, PartialOrd, Ord, Eq, PartialEq)] +struct DentryKey { + name: String, + parent_ptr: usize, +} + +impl DentryKey { + pub fn new(name: &str, parent: &Arc) -> Self { + Self { + name: String::from(name), + parent_ptr: Arc::as_ptr(parent) as usize, + } + } +} + +fn write_lock_children_on_two_dentries<'a>( this: &'a Dentry, other: &'a Dentry, -) -> (RwLockWriteGuard<'a, Dentry_>, RwLockWriteGuard<'a, Dentry_>) { - let this_ptr = Arc::as_ptr(&this.this()); - let other_ptr = Arc::as_ptr(&other.this()); - if this_ptr < other_ptr { - let this = this.inner.write(); - let other = other.inner.write(); +) -> (MutexGuard<'a, Children>, MutexGuard<'a, Children>) { + let this_key = this.key(); + let other_key = other.key(); + if this_key < other_key { + let this = this.children.lock(); + let other = other.children.lock(); (this, other) } else { - let other = other.inner.write(); - let this = this.inner.write(); + let other = other.children.lock(); + let this = this.children.lock(); (this, other) } }