Procházet zdrojové kódy

vfs, rcu: rework path walking with new rcu syntax

The old path walking algorithm requires recursion, which is not
supported in async rust. So we boxed them all as a temporary solution in
previous commits. This would introduce mass overhead even for fast path
walks just because we might sleep in `readlink()` and `lookup()` calls.

The new proposed method is to break the walk into several phases similar
to that in Linux: RCU walk and REF walk. The RCU walk will hold the RCU
lock and never blocks so the function itself can be non-async. If we hit
non-present dentries, we will fallback to REF walk. In REF walks, we
clone the Arcs and consult to the VFS layer for an accurate answer.

Note that in both the two methods mentioned above, symlinks are not
handled and will be returned directly with all path components left
untouched. We have a dedicated async function to follow the symlinks by
recursive calling the walk function. This can be slow and won't be
called frequently. So we wrapped the function with `Box::pin()` to break
the recursion chain. After the symlink resolution is done, we return to
the original position and continue the walk.

We found that the association of an inode to a dentry is one way. So the
`data` RCUPointer field is actually unnecessary and we can use the atomic
dentry type to sync readers with the writer. This way we can eliminate
`DentryData` allocations and improve performance.

We also introduced a new RCU read lock syntax. In the RCU walk mentioned
above, we need to store dentry references protected by some RCU read lock.
With the old syntax, we can't express the lifetime associtated by the
common RCU read lock. The new syntax provides a `rcu_read_lock()` method
to acquire the RCU read lock. The lock returned has a associated lifetime
so we can use it in the RCU session.

Signed-off-by: greatbridf <greatbridf@icloud.com>
greatbridf před 4 měsíci
rodič
revize
ea2122331e

+ 5 - 0
Cargo.lock

@@ -28,6 +28,10 @@ dependencies = [
  "log",
 ]
 
+[[package]]
+name = "arcref"
+version = "0.1.0"
+
 [[package]]
 name = "async-trait"
 version = "0.1.89"
@@ -155,6 +159,7 @@ dependencies = [
  "acpi",
  "align_ext",
  "another_ext4",
+ "arcref",
  "async-trait",
  "atomic_unique_refcell",
  "bitflags",

+ 7 - 1
Cargo.toml

@@ -7,6 +7,9 @@ edition = "2021"
 crate-type = ["bin"]
 
 [dependencies]
+arcref = { path = "./crates/arcref", default-features = false, features = [
+    "alloc",
+] }
 atomic_unique_refcell = { path = "./crates/atomic_unique_refcell", features = [
     "no_std",
 ] }
@@ -35,7 +38,10 @@ stalloc = { version = "0.6.1", default-features = false, features = [
     "allocator-api",
 ] }
 async-trait = "0.1.89"
-futures = { version = "0.3.31", features = ["alloc", "async-await"], default-features = false }
+futures = { version = "0.3.31", features = [
+    "alloc",
+    "async-await",
+], default-features = false }
 
 [target.'cfg(any(target_arch = "riscv64", target_arch = "loongarch64"))'.dependencies]
 virtio-drivers = { version = "0.11.0" }

+ 7 - 0
crates/arcref/Cargo.lock

@@ -0,0 +1,7 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 4
+
+[[package]]
+name = "arcref"
+version = "0.1.0"

+ 11 - 0
crates/arcref/Cargo.toml

@@ -0,0 +1,11 @@
+[package]
+name = "arcref"
+version = "0.1.0"
+edition = "2024"
+
+[dependencies]
+
+[features]
+alloc = []
+std = ["alloc"]
+default = ["std"]

+ 216 - 0
crates/arcref/src/arcref.rs

@@ -0,0 +1,216 @@
+#[cfg(not(feature = "std"))]
+use core::{
+    borrow::Borrow,
+    marker::{PhantomData, Unsize},
+    mem::ManuallyDrop,
+    ops::{Deref, DispatchFromDyn},
+};
+
+#[cfg(all(not(feature = "std"), feature = "alloc"))]
+extern crate alloc;
+
+#[cfg(all(not(feature = "std"), feature = "alloc"))]
+use alloc::sync::Arc;
+
+#[cfg(feature = "std")]
+use std::{
+    borrow::Borrow,
+    marker::{PhantomData, Unsize},
+    mem::ManuallyDrop,
+    ops::{Deref, DispatchFromDyn},
+    sync::Arc,
+};
+
+pub trait AsArcRef<T>
+where
+    T: ?Sized,
+{
+    /// Borrow the [`Arc`] and convert the reference into [`ArcRef`].
+    fn aref(&self) -> ArcRef<'_, T>;
+}
+
+pub struct ArcRef<'a, T: ?Sized> {
+    ptr: *const T,
+    _phantom: PhantomData<&'a ()>,
+}
+
+unsafe impl<T: ?Sized + Send + Sync> Send for ArcRef<'_, T> {}
+unsafe impl<T: ?Sized + Send + Sync> Sync for ArcRef<'_, T> {}
+
+#[cfg(any(feature = "std", feature = "alloc"))]
+impl<'a, T: ?Sized> ArcRef<'a, T> {
+    pub fn new(arc: &'a Arc<T>) -> Self {
+        Self {
+            ptr: Arc::as_ptr(arc),
+            _phantom: PhantomData,
+        }
+    }
+
+    /// Create a new `ArcRef` from a raw pointer.
+    ///
+    /// # Safety
+    /// The given pointer MUST be created by `Arc::as_ptr` or `Arc::into_raw`.
+    /// The caller is responsible to ensure that the pointer is valid for the
+    /// lifetime of the `ArcRef`.
+    pub unsafe fn new_unchecked(arc_ptr: *const T) -> Self {
+        Self {
+            ptr: arc_ptr,
+            _phantom: PhantomData,
+        }
+    }
+
+    pub fn with_arc<Func, Out>(self, func: Func) -> Out
+    where
+        Func: FnOnce(&Arc<T>) -> Out,
+    {
+        func(&ManuallyDrop::new(unsafe { Arc::from_raw(self.ptr) }))
+    }
+
+    pub fn clone_arc(self) -> Arc<T> {
+        self.with_arc(|arc| arc.clone())
+    }
+
+    pub fn ptr_eq_arc(self, other: &Arc<T>) -> bool {
+        self.with_arc(|arc| Arc::ptr_eq(arc, other))
+    }
+}
+
+#[cfg(all(not(feature = "std"), feature = "alloc"))]
+impl<T> AsArcRef<T> for Arc<T>
+where
+    T: ?Sized,
+{
+    fn aref(&self) -> ArcRef<'_, T> {
+        ArcRef::new(self)
+    }
+}
+
+impl<T> AsRef<T> for ArcRef<'_, T>
+where
+    T: ?Sized,
+{
+    fn as_ref(&self) -> &T {
+        self.deref()
+    }
+}
+
+impl<T> Borrow<T> for ArcRef<'_, T>
+where
+    T: ?Sized,
+{
+    fn borrow(&self) -> &T {
+        self.deref()
+    }
+}
+
+impl<'a, T> Clone for ArcRef<'a, T>
+where
+    T: ?Sized,
+{
+    fn clone(&self) -> Self {
+        Self {
+            ptr: self.ptr,
+            _phantom: PhantomData,
+        }
+    }
+}
+
+impl<T> Copy for ArcRef<'_, T> where T: ?Sized {}
+
+impl<T: ?Sized> Deref for ArcRef<'_, T> {
+    type Target = T;
+
+    fn deref(&self) -> &T {
+        unsafe {
+            // SAFETY: `self.ptr` points to a valid `T` instance because it was
+            //         created from a valid `Arc<T>`.
+            self.ptr.as_ref().unwrap_unchecked()
+        }
+    }
+}
+
+impl<'a, T, U> DispatchFromDyn<ArcRef<'a, U>> for ArcRef<'a, T>
+where
+    T: ?Sized + Unsize<U>,
+    U: ?Sized,
+{
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn create_from_arc() {
+        let data = Arc::new(42);
+        let _arc_ref = ArcRef::new(&data);
+    }
+
+    #[test]
+    fn deref() {
+        let data = Arc::new(42);
+        let arc_ref = ArcRef::new(&data);
+
+        assert_eq!(*arc_ref, 42);
+    }
+
+    #[test]
+    fn clone_into_arc() {
+        let data = Arc::new(42);
+        let arc_ref = ArcRef::new(&data);
+
+        let cloned = arc_ref.clone_arc();
+
+        assert_eq!(Arc::strong_count(&data), 2);
+        assert_eq!(*cloned, 42);
+    }
+
+    #[test]
+    fn dyn_compatible_receiver() {
+        struct Data(u32);
+
+        trait Trait {
+            fn foo(self: ArcRef<Self>) -> u32;
+        }
+
+        impl Trait for Data {
+            fn foo(self: ArcRef<Self>) -> u32 {
+                self.0
+            }
+        }
+
+        let data = Arc::new(Data(42));
+        let arc_ref = ArcRef::new(&data);
+
+        assert_eq!(arc_ref.foo(), 42);
+    }
+
+    #[test]
+    fn clone_from_train_methods() {
+        struct Data(u32);
+
+        trait Trait {
+            fn foo(&self) -> u32;
+
+            fn clone_self(self: ArcRef<Self>) -> Arc<dyn Trait>;
+        }
+
+        impl Trait for Data {
+            fn foo(&self) -> u32 {
+                self.0
+            }
+
+            fn clone_self(self: ArcRef<Self>) -> Arc<dyn Trait> {
+                self.clone_arc() as _
+            }
+        }
+
+        let data = Arc::new(Data(42));
+        let arc_ref = ArcRef::new(&data);
+
+        let cloned = arc_ref.clone_self();
+
+        assert_eq!(arc_ref.foo(), 42);
+        assert_eq!(cloned.foo(), 42);
+    }
+}

+ 8 - 0
crates/arcref/src/lib.rs

@@ -0,0 +1,8 @@
+#![cfg_attr(not(feature = "std"), no_std)]
+#![feature(arbitrary_self_types)]
+#![feature(dispatch_from_dyn)]
+#![feature(unsize)]
+
+mod arcref;
+
+pub use arcref::{ArcRef, AsArcRef};

+ 6 - 0
crates/posix_types/src/result.rs

@@ -1,14 +1,18 @@
 pub enum PosixError {
+    ENOENT = 2,
     EFAULT = 14,
     EXDEV = 18,
+    ENOTDIR = 20,
     EINVAL = 22,
 }
 
 impl From<PosixError> for u32 {
     fn from(error: PosixError) -> Self {
         match error {
+            PosixError::ENOENT => 2,
             PosixError::EFAULT => 14,
             PosixError::EXDEV => 18,
+            PosixError::ENOTDIR => 20,
             PosixError::EINVAL => 22,
         }
     }
@@ -17,8 +21,10 @@ impl From<PosixError> for u32 {
 impl core::fmt::Debug for PosixError {
     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
         match self {
+            Self::ENOENT => write!(f, "ENOENT"),
             Self::EFAULT => write!(f, "EFAULT"),
             Self::EXDEV => write!(f, "EXDEV"),
+            Self::ENOTDIR => write!(f, "ENOTDIR"),
             Self::EINVAL => write!(f, "EINVAL"),
         }
     }

+ 168 - 226
src/kernel/vfs/dentry.rs

@@ -1,45 +1,62 @@
 pub mod dcache;
+mod walk;
 
-use super::{
-    inode::{Ino, Inode, InodeUse, RenameData, WriteOffset},
-    types::{DeviceId, Format, Mode, Permission},
-    FsContext,
-};
-use crate::{
-    hash::KernelHasher,
-    io::{Buffer, ByteBuffer},
-    kernel::{block::BlockDevice, CharDevice},
-    path::{Path, PathComponent},
-    prelude::*,
-    rcu::{RCUNode, RCUPointer, RCUReadGuard},
-};
-use crate::{
-    io::Stream,
-    kernel::constants::{EEXIST, EINVAL, EISDIR, ELOOP, ENOENT, ENOTDIR, EPERM, ERANGE},
-};
-use alloc::sync::Arc;
 use core::{
+    cell::UnsafeCell,
     fmt,
-    future::Future,
     hash::{BuildHasher, BuildHasherDefault, Hasher},
-    pin::Pin,
-    sync::atomic::{AtomicPtr, AtomicU64, Ordering},
+    sync::atomic::{AtomicPtr, AtomicU64, AtomicU8, Ordering},
 };
+
+use alloc::sync::Arc;
+use arcref::AsArcRef;
 use eonix_sync::LazyLock;
 use pointers::BorrowedArc;
 use posix_types::{namei::RenameFlags, open::OpenFlags, result::PosixError, stat::StatX};
 
-#[derive(PartialEq, Eq)]
+use crate::{
+    hash::KernelHasher,
+    io::Buffer,
+    io::Stream,
+    kernel::constants::{EEXIST, EINVAL, EISDIR, ELOOP, ENOENT, EPERM, ERANGE},
+    kernel::{block::BlockDevice, CharDevice},
+    path::Path,
+    prelude::*,
+    rcu::{rcu_read_lock, RCUNode, RCUPointer, RCUReadGuard},
+};
+
+use super::{
+    inode::{Ino, Inode, InodeUse, RenameData, WriteOffset},
+    types::{DeviceId, Format, Mode, Permission},
+    FsContext,
+};
+
+const D_INVALID: u8 = 0;
+const D_REGULAR: u8 = 1;
+const D_DIRECTORY: u8 = 2;
+const D_SYMLINK: u8 = 3;
+
+#[derive(Debug, PartialEq, Eq)]
 enum DentryKind {
-    Regular,
-    Directory,
-    Symlink,
-    Mountpoint,
+    Regular = D_REGULAR as isize,
+    Directory = D_DIRECTORY as isize,
+    Symlink = D_SYMLINK as isize,
 }
 
-struct DentryData {
-    inode: InodeUse<dyn Inode>,
-    kind: DentryKind,
+/// The [`Inode`] associated with a [`Dentry`].
+///
+/// We could assign an inode to a negative dentry exactly once when the dentry
+/// is invalid and we create a file or directory on it, or the dentry is brought
+/// to the dcache by [lookup()].
+///
+/// This guarantees that as long as we acquire a non-invalid from [`Self::kind`],
+/// we are synced with the writer and can safely read the [`Self::inode`] field
+/// without reading torn data.
+///
+/// [lookup()]: crate::kernel::vfs::inode::InodeDirOps::lookup
+struct AssociatedInode {
+    kind: UnsafeCell<Option<DentryKind>>,
+    inode: UnsafeCell<Option<InodeUse<dyn Inode>>>,
 }
 
 /// # Safety
@@ -58,8 +75,7 @@ pub struct Dentry {
     prev: AtomicPtr<Dentry>,
     next: AtomicPtr<Dentry>,
 
-    // RCU Mutable
-    data: RCUPointer<DentryData>,
+    inode: AssociatedInode,
 }
 
 pub(super) static DROOT: LazyLock<Arc<Dentry>> = LazyLock::new(|| {
@@ -69,7 +85,7 @@ pub(super) static DROOT: LazyLock<Arc<Dentry>> = LazyLock::new(|| {
         hash: AtomicU64::new(0),
         prev: AtomicPtr::default(),
         next: AtomicPtr::default(),
-        data: RCUPointer::empty(),
+        inode: AssociatedInode::new(),
     });
 
     unsafe {
@@ -119,50 +135,19 @@ impl Dentry {
 
         self.hash.store(hash, Ordering::Relaxed);
     }
-
-    async fn find(self: &Arc<Self>, name: &[u8]) -> KResult<Arc<Self>> {
-        let data = self.data.load();
-        let data = data.as_ref().ok_or(ENOENT)?;
-
-        if data.kind != DentryKind::Directory {
-            return Err(ENOTDIR);
-        }
-
-        match name {
-            b"." => Ok(self.clone()),
-            b".." => Ok(self.parent().clone()),
-            _ => {
-                let dentry = Dentry::create(self.clone(), name);
-
-                if let Some(found) = dcache::d_find_fast(&dentry) {
-                    unsafe {
-                        // SAFETY: This is safe because the dentry is never shared with
-                        //         others so we can drop them safely.
-                        let _ = dentry.name.swap(None);
-                        let _ = dentry.parent.swap(None);
-                    }
-
-                    return Ok(found);
-                }
-
-                let _ = dcache::d_try_revalidate(&dentry).await;
-                dcache::d_add(dentry.clone());
-
-                Ok(dentry)
-            }
-        }
-    }
 }
 
 impl Dentry {
     pub fn create(parent: Arc<Dentry>, name: &[u8]) -> Arc<Self> {
+        // TODO!!!: don't acquire our parent's refcount here...
+
         let val = Arc::new(Self {
             parent: RCUPointer::new(parent),
             name: RCUPointer::new(Arc::new(Arc::from(name))),
             hash: AtomicU64::new(0),
             prev: AtomicPtr::default(),
             next: AtomicPtr::default(),
-            data: RCUPointer::empty(),
+            inode: AssociatedInode::new(),
         });
 
         val.rehash();
@@ -196,27 +181,12 @@ impl Dentry {
             .map_or(core::ptr::null(), |parent| Arc::as_ptr(&parent))
     }
 
-    fn save(&self, inode: InodeUse<dyn Inode>, kind: DentryKind) {
-        let new = DentryData { inode, kind };
-
-        // TODO!!!: We don't actually need to use `RCUPointer` here
-        // Safety: this function may only be called from `create`-like functions which requires the
-        // superblock's write locks to be held, so only one creation can happen at a time and we
-        // can't get a reference to the old data.
-        let old = unsafe { self.data.swap(Some(Arc::new(new))) };
-        assert!(old.is_none());
-    }
-
     pub fn fill(&self, file: InodeUse<dyn Inode>) {
-        match file.format() {
-            Format::REG | Format::BLK | Format::CHR => self.save(file, DentryKind::Regular),
-            Format::DIR => self.save(file, DentryKind::Directory),
-            Format::LNK => self.save(file, DentryKind::Symlink),
-        }
+        self.inode.store(file);
     }
 
     pub fn inode(&self) -> Option<InodeUse<dyn Inode>> {
-        self.data.load().as_ref().map(|data| data.inode.clone())
+        self.inode.load().map(|(_, inode)| inode.clone())
     }
 
     pub fn get_inode(&self) -> KResult<InodeUse<dyn Inode>> {
@@ -224,181 +194,85 @@ impl Dentry {
     }
 
     pub fn is_directory(&self) -> bool {
-        let data = self.data.load();
-        data.as_ref()
-            .map_or(false, |data| data.kind == DentryKind::Directory)
+        self.inode
+            .load()
+            .map_or(false, |(kind, _)| kind == DentryKind::Directory)
     }
 
     pub fn is_valid(&self) -> bool {
-        self.data.load().is_some()
+        self.inode.load().is_some()
     }
 
     pub async fn open_check(self: &Arc<Self>, flags: OpenFlags, perm: Permission) -> KResult<()> {
-        let data = self.data.load();
-
-        if data.is_some() {
-            if flags.contains(OpenFlags::O_CREAT | OpenFlags::O_EXCL) {
-                Err(EEXIST)
-            } else {
-                Ok(())
-            }
-        } else {
-            if !flags.contains(OpenFlags::O_CREAT) {
-                return Err(ENOENT);
-            }
-
-            let parent = self.parent().get_inode()?;
-            parent.create(self, perm).await
-        }
-    }
-}
-
-impl Dentry {
-    fn resolve_directory(
-        context: &FsContext,
-        dentry: Arc<Self>,
-        nrecur: u32,
-    ) -> Pin<Box<impl Future<Output = KResult<Arc<Self>>> + use<'_>>> {
-        Box::pin(async move {
-            if nrecur >= 16 {
-                return Err(ELOOP);
-            }
-
-            let data = dentry.data.load();
-            let data = data.as_ref().ok_or(ENOENT)?;
-
-            match data.kind {
-                DentryKind::Regular => Err(ENOTDIR),
-                DentryKind::Directory => Ok(dentry),
-                DentryKind::Symlink => {
-                    let mut buffer = [0u8; 256];
-                    let mut buffer = ByteBuffer::new(&mut buffer);
-
-                    data.inode.readlink(&mut buffer).await?;
-                    let path = Path::new(buffer.data())?;
-
-                    let dentry =
-                        Self::open_recursive(context, &dentry.parent(), path, true, nrecur + 1)
-                            .await?;
-
-                    Self::resolve_directory(context, dentry, nrecur + 1).await
+        match self.inode.load() {
+            Some(_) => {
+                if flags.contains(OpenFlags::O_CREAT | OpenFlags::O_EXCL) {
+                    Err(EEXIST)
+                } else {
+                    Ok(())
                 }
-                _ => panic!("Invalid dentry flags"),
-            }
-        })
-    }
-
-    pub fn open_recursive<'r, 'a: 'r, 'b: 'r, 'c: 'r>(
-        context: &'a FsContext,
-        cwd: &'b Arc<Self>,
-        path: Path<'c>,
-        follow: bool,
-        nrecur: u32,
-    ) -> Pin<Box<impl Future<Output = KResult<Arc<Self>>> + 'r>> {
-        Box::pin(async move {
-            // too many recursive search layers will cause stack overflow
-            // so we use 16 for now
-            if nrecur >= 16 {
-                return Err(ELOOP);
             }
-
-            let mut cwd = if path.is_absolute() {
-                context.fsroot.clone()
-            } else {
-                cwd.clone()
-            };
-
-            for item in path.iter() {
-                if let PathComponent::TrailingEmpty = item {
-                    if cwd.data.load().as_ref().is_none() {
-                        return Ok(cwd);
-                    }
+            None => {
+                if !flags.contains(OpenFlags::O_CREAT) {
+                    return Err(ENOENT);
                 }
 
-                cwd = Self::resolve_directory(context, cwd, nrecur).await?;
-
-                match item {
-                    PathComponent::TrailingEmpty | PathComponent::Current => {} // pass
-                    PathComponent::Parent => {
-                        if !cwd.hash_eq(&context.fsroot) {
-                            let parent = cwd.parent().clone();
-                            cwd = Self::resolve_directory(context, parent, nrecur).await?;
-                        }
-                        continue;
-                    }
-                    PathComponent::Name(name) => {
-                        cwd = cwd.find(name).await?;
-                    }
-                }
+                let parent = self.parent().get_inode()?;
+                parent.create(self, perm).await
             }
-
-            if follow {
-                let data = cwd.data.load();
-
-                if let Some(data) = data.as_ref() {
-                    if data.kind == DentryKind::Symlink {
-                        let data = cwd.data.load();
-                        let data = data.as_ref().unwrap();
-                        let mut buffer = [0u8; 256];
-                        let mut buffer = ByteBuffer::new(&mut buffer);
-
-                        data.inode.readlink(&mut buffer).await?;
-                        let path = Path::new(buffer.data())?;
-
-                        let parent = cwd.parent().clone();
-                        cwd =
-                            Self::open_recursive(context, &parent, path, true, nrecur + 1).await?;
-                    }
-                }
-            }
-
-            Ok(cwd)
-        })
+        }
     }
+}
 
+impl Dentry {
     pub async fn open(
         context: &FsContext,
-        path: Path<'_>,
+        path: &Path,
         follow_symlinks: bool,
     ) -> KResult<Arc<Self>> {
         let cwd = context.cwd.lock().clone();
-        Dentry::open_recursive(context, &cwd, path, follow_symlinks, 0).await
+        Self::open_at(context, &cwd, path, follow_symlinks).await
     }
 
     pub async fn open_at(
         context: &FsContext,
         at: &Arc<Self>,
-        path: Path<'_>,
+        path: &Path,
         follow_symlinks: bool,
     ) -> KResult<Arc<Self>> {
-        Dentry::open_recursive(context, at, path, follow_symlinks, 0).await
-    }
+        let mut found = context.start_recursive_walk(at, path).await?;
 
-    pub fn get_path(
-        self: &Arc<Dentry>,
-        context: &FsContext,
-        buffer: &mut dyn Buffer,
-    ) -> KResult<()> {
-        let locked_parent = self.parent();
+        if !follow_symlinks {
+            return Ok(found);
+        }
 
-        let path = {
-            let mut path = vec![];
+        loop {
+            match found.inode.load() {
+                Some((DentryKind::Symlink, inode)) => {
+                    found = context.follow_symlink(found.aref(), inode, 0).await?;
+                }
+                _ => return Ok(found),
+            }
+        }
+    }
 
-            let mut parent = locked_parent.borrow();
-            let mut dentry = BorrowedArc::new(self);
+    pub fn get_path(self: &Arc<Self>, context: &FsContext, buffer: &mut dyn Buffer) -> KResult<()> {
+        let rcu_read = rcu_read_lock();
 
-            while Arc::as_ptr(&dentry) != Arc::as_ptr(&context.fsroot) {
-                if path.len() > 32 {
-                    return Err(ELOOP);
-                }
+        let mut path = vec![];
+
+        let mut current = self.aref();
+        let mut parent = self.parent.dereference(&rcu_read).unwrap();
 
-                path.push(dentry.name().clone());
-                dentry = parent;
-                parent = dentry.parent.load_protected(&locked_parent).unwrap();
+        while !current.ptr_eq_arc(&context.fsroot) {
+            if path.len() > 32 {
+                return Err(ELOOP);
             }
 
-            path
-        };
+            path.push(current.name.dereference(&rcu_read).unwrap());
+            current = parent;
+            parent = current.parent.dereference(&rcu_read).unwrap();
+        }
 
         buffer.fill(b"/")?.ok_or(ERANGE)?;
         for item in path.iter().rev().map(|name| name.as_ref()) {
@@ -533,3 +407,71 @@ impl Dentry {
         old_parent.rename(rename_data).await
     }
 }
+
+impl DentryKind {
+    fn into_raw(self) -> u8 {
+        unsafe { core::mem::transmute(self) }
+    }
+
+    fn from_raw(raw: u8) -> Option<Self> {
+        unsafe { core::mem::transmute(raw) }
+    }
+
+    fn as_atomic(me: &UnsafeCell<Option<Self>>) -> &AtomicU8 {
+        unsafe { AtomicU8::from_ptr(me.get().cast()) }
+    }
+
+    fn atomic_acq(me: &UnsafeCell<Option<Self>>) -> Option<Self> {
+        Self::from_raw(Self::as_atomic(me).load(Ordering::Acquire))
+    }
+
+    fn atomic_swap_acqrel(me: &UnsafeCell<Option<Self>>, kind: Option<Self>) -> Option<Self> {
+        Self::from_raw(Self::as_atomic(me).swap(kind.map_or(0, Self::into_raw), Ordering::AcqRel))
+    }
+}
+
+impl AssociatedInode {
+    fn new() -> Self {
+        Self {
+            inode: UnsafeCell::new(None),
+            kind: UnsafeCell::new(None),
+        }
+    }
+
+    fn store(&self, inode: InodeUse<dyn Inode>) {
+        let kind = match inode.format() {
+            Format::REG | Format::BLK | Format::CHR => DentryKind::Regular,
+            Format::DIR => DentryKind::Directory,
+            Format::LNK => DentryKind::Symlink,
+        };
+
+        unsafe {
+            // SAFETY: We should be the first and only one to store the inode as
+            //         is checked below. All other readers reading non-invalid
+            //         kind will see the fully written inode.
+            self.inode.get().write(Some(inode));
+        }
+
+        assert_eq!(
+            DentryKind::atomic_swap_acqrel(&self.kind, Some(kind)),
+            None,
+            "Dentry can only be stored once."
+        );
+    }
+
+    fn kind(&self) -> Option<DentryKind> {
+        DentryKind::atomic_acq(&self.kind)
+    }
+
+    fn load(&self) -> Option<(DentryKind, &InodeUse<dyn Inode>)> {
+        self.kind().map(|kind| unsafe {
+            let inode = (&*self.inode.get())
+                .as_ref()
+                .expect("Dentry with non-invalid kind has no inode");
+            (kind, inode)
+        })
+    }
+}
+
+unsafe impl Send for AssociatedInode {}
+unsafe impl Sync for AssociatedInode {}

+ 65 - 11
src/kernel/vfs/dentry/dcache.rs

@@ -1,11 +1,13 @@
 use super::Dentry;
 use crate::kernel::constants::ENOENT;
-use crate::rcu::RCUPointer;
+use crate::rcu::{RCUPointer, RCUReadLock};
 use crate::{
     prelude::*,
     rcu::{RCUIterator, RCUList},
 };
 use alloc::sync::Arc;
+use arcref::ArcRef;
+use core::ops::Deref;
 use core::sync::atomic::Ordering;
 use eonix_sync::Mutex;
 
@@ -16,26 +18,47 @@ static DCACHE: [RCUList<Dentry>; 1 << DCACHE_HASH_BITS] =
 
 static D_EXCHANGE_LOCK: Mutex<()> = Mutex::new(());
 
-pub fn d_hinted(dentry: &Dentry) -> &'static RCUList<Dentry> {
-    let hash = dentry.hash.load(Ordering::Relaxed) as usize & ((1 << DCACHE_HASH_BITS) - 1);
+pub trait DCacheItem {
+    fn d_hash(&self) -> usize;
+    fn d_parent(&self) -> *const Dentry;
+    fn d_name<'r, 'a: 'r, 'b: 'a>(
+        &'a self,
+        rcu_read: &'b RCUReadLock,
+    ) -> impl Deref<Target = [u8]> + 'r;
+}
+
+fn d_eq(lhs: &impl DCacheItem, rhs: &impl DCacheItem, rcu_read: &RCUReadLock) -> bool {
+    lhs.d_hash() == rhs.d_hash()
+        && lhs.d_parent() == rhs.d_parent()
+        && *lhs.d_name(rcu_read) == *rhs.d_name(rcu_read)
+}
+
+fn d_hinted(item: &impl DCacheItem) -> &'static RCUList<Dentry> {
+    let hash = item.d_hash() & ((1 << DCACHE_HASH_BITS) - 1);
     &DCACHE[hash]
 }
 
-pub fn d_iter_for(dentry: &Dentry) -> RCUIterator<'static, Dentry> {
-    d_hinted(dentry).iter()
+fn d_iter_for<'rcu>(
+    item: &impl DCacheItem,
+    rcu_read: &'rcu RCUReadLock,
+) -> RCUIterator<'static, 'rcu, Dentry> {
+    d_hinted(item).iter(rcu_read)
+}
+
+pub fn d_find_rcu<'rcu>(
+    item: &impl DCacheItem,
+    rcu_read: &'rcu RCUReadLock,
+) -> Option<ArcRef<'rcu, Dentry>> {
+    d_iter_for(item, rcu_read).find(|cur_ref| cur_ref.with_arc(|cur| d_eq(cur, item, rcu_read)))
 }
 
 /// Add the dentry to the dcache
 pub fn d_add(dentry: Arc<Dentry>) {
+    // TODO: Add `children` field to parent and lock parent dentry to avoid
+    //       concurrent insertion causing duplication.
     d_hinted(&dentry).insert(dentry);
 }
 
-pub fn d_find_fast(dentry: &Dentry) -> Option<Arc<Dentry>> {
-    d_iter_for(dentry)
-        .find(|cur| cur.hash_eq(dentry))
-        .map(|dentry| dentry.clone())
-}
-
 /// Call `lookup()` on the parent inode to try find if the dentry points to a valid inode
 ///
 /// Silently fail without any side effects
@@ -80,3 +103,34 @@ pub async fn d_exchange(old: &Arc<Dentry>, new: &Arc<Dentry>) {
     d_add(old.clone());
     d_add(new.clone());
 }
+
+impl DCacheItem for Arc<Dentry> {
+    fn d_hash(&self) -> usize {
+        self.hash.load(Ordering::Relaxed) as usize
+    }
+
+    fn d_parent(&self) -> *const Dentry {
+        self.parent_addr()
+    }
+
+    fn d_name<'r, 'a: 'r, 'b: 'a>(
+        &'a self,
+        rcu_read: &'b RCUReadLock,
+    ) -> impl Deref<Target = [u8]> + 'r {
+        struct Name<'a>(ArcRef<'a, Arc<[u8]>>);
+
+        impl Deref for Name<'_> {
+            type Target = [u8];
+
+            fn deref(&self) -> &Self::Target {
+                &self.0
+            }
+        }
+
+        Name(
+            self.name
+                .dereference(rcu_read)
+                .expect("Dentry should have a non-null name"),
+        )
+    }
+}

+ 370 - 0
src/kernel/vfs/dentry/walk.rs

@@ -0,0 +1,370 @@
+use core::{
+    future::Future,
+    hash::{BuildHasher, BuildHasherDefault, Hasher},
+    ops::Deref,
+    pin::Pin,
+};
+
+use alloc::{boxed::Box, sync::Arc};
+use arcref::{ArcRef, AsArcRef};
+use posix_types::result::PosixError;
+
+use crate::{
+    hash::KernelHasher,
+    io::ByteBuffer,
+    kernel::{
+        constants::ELOOP,
+        vfs::{
+            inode::{Inode, InodeUse},
+            FsContext,
+        },
+    },
+    path::{Path, PathComponent, PathIterator},
+    prelude::KResult,
+    rcu::{rcu_read_lock, RCUReadLock},
+};
+
+use super::{
+    dcache::{self, DCacheItem},
+    Dentry, DentryKind,
+};
+
+struct DentryFind<'a, 'b> {
+    parent: &'a Dentry,
+    name: &'b [u8],
+    hash: usize,
+}
+
+pub enum WalkResultRcu<'rcu, 'path> {
+    Err(PosixError),
+    Ok(ArcRef<'rcu, Dentry>),
+    Symlink {
+        symlink: ArcRef<'rcu, Dentry>,
+        inode: InodeUse<dyn Inode>,
+    },
+    Miss {
+        parent: ArcRef<'rcu, Dentry>,
+        name: &'path [u8],
+    },
+}
+
+pub enum WalkResult {
+    Err(PosixError),
+    Ok(Arc<Dentry>),
+    Symlink {
+        symlink: Arc<Dentry>,
+        inode: InodeUse<dyn Inode>,
+    },
+}
+
+impl Dentry {
+    /// Quick path of the dentry find operation.
+    ///
+    /// Check invalid and non-directory dentries, return immediately on dot and
+    /// dotdot component, and do a quick rcu dcache lookup.
+    ///
+    /// Note that while `Some(dentry)` guarantees present and valid dentry,
+    /// returning `None` is acceptable if the actual file exists but is not in
+    /// the dentry cache. If so, we should check again with `lookup`.
+    fn find_rcu<'r, 's: 'r>(
+        self: ArcRef<'s, Self>,
+        name: &[u8],
+        rcu_read: &'r RCUReadLock,
+    ) -> Result<Option<ArcRef<'r, Self>>, PosixError> {
+        match self.inode.load() {
+            Some((DentryKind::Directory, _)) => {}
+            Some(_) => return Err(PosixError::ENOTDIR),
+            None => return Err(PosixError::ENOENT),
+        }
+
+        match name {
+            b"." => Ok(Some(self)),
+            b".." => Ok(Some(
+                self.parent
+                    .dereference(rcu_read)
+                    .expect("The field `parent` should be non-null"),
+            )),
+            _ => {
+                let dentry_find = DentryFind::new(&self, name);
+                Ok(dcache::d_find_rcu(&dentry_find, rcu_read))
+            }
+        }
+    }
+
+    async fn find_slow(self: &Arc<Self>, name: &[u8]) -> Result<Arc<Self>, PosixError> {
+        let dentry = Dentry::create(self.clone(), name);
+
+        let _ = dcache::d_try_revalidate(&dentry).await;
+        dcache::d_add(dentry.clone());
+
+        Ok(dentry)
+    }
+
+    pub async fn find_full(self: &Arc<Self>, name: &[u8]) -> Result<Arc<Self>, PosixError> {
+        if let Some(dentry) = self.aref().find_rcu(name, &rcu_read_lock())? {
+            return Ok(dentry.clone_arc());
+        }
+
+        self.find_slow(name).await
+    }
+}
+
+impl FsContext {
+    /// Walk the pathname and try to find the corresponding dentry FAST without
+    /// consulting the VFS for invalid dentries encountered.
+    fn walk_rcu<'rcu, 'path>(
+        &self,
+        mut current: ArcRef<'rcu, Dentry>,
+        iter: &mut PathIterator<'path>,
+        rcu_read: &'rcu RCUReadLock,
+    ) -> WalkResultRcu<'rcu, 'path> {
+        use PathComponent::*;
+
+        loop {
+            let inode = current.inode.load();
+
+            if iter.is_empty() {
+                break;
+            }
+
+            // Skip symlink resolution in rcu walk without consuming the iter.
+            if let Some((DentryKind::Symlink, inode)) = inode {
+                return WalkResultRcu::Symlink {
+                    symlink: current,
+                    inode: inode.clone(),
+                };
+            }
+
+            let Some(component) = iter.next() else {
+                break;
+            };
+
+            match (inode, component) {
+                // Skip trailing empty and dot for normal directories.
+                (Some((DentryKind::Directory, _)), TrailingEmpty | Current) => {}
+                // Walk to parent directory unless we are at the filesystem root.
+                (Some((DentryKind::Directory, _)), Parent) => {
+                    if current.ptr_eq_arc(&self.fsroot) {
+                        continue;
+                    }
+
+                    current = current
+                        .parent
+                        .dereference(&rcu_read)
+                        .expect("parent should exist");
+                }
+                // Normal directory traversal
+                (Some((DentryKind::Directory, _)), Name(name)) => {
+                    match current.find_rcu(name, &rcu_read) {
+                        Err(err) => return WalkResultRcu::Err(err),
+                        Ok(Some(found)) => {
+                            current = found;
+                        }
+                        Ok(None) => {
+                            return WalkResultRcu::Miss {
+                                name,
+                                parent: current,
+                            };
+                        }
+                    }
+                }
+                // Not a directory, fail and exit.
+                (Some(_), _) => return WalkResultRcu::Err(PosixError::ENOTDIR),
+                // Return invalid trailing entries directly.
+                (None, TrailingEmpty) => return WalkResultRcu::Ok(current),
+                // Invalid intermediate entries are not acceptable.
+                (None, _) => return WalkResultRcu::Err(PosixError::ENOENT),
+            }
+        }
+
+        WalkResultRcu::Ok(current)
+    }
+
+    /// Walk the pathname slowly with refcounts held and VFS lookups.
+    async fn walk_slow(&self, mut current: Arc<Dentry>, iter: &mut PathIterator<'_>) -> WalkResult {
+        use PathComponent::*;
+
+        loop {
+            // `current` should be the parent directory and `component` is the
+            // next path component we are stepping into.
+
+            if iter.is_empty() {
+                break;
+            }
+
+            if let Some((DentryKind::Symlink, inode)) = current.inode.load() {
+                return WalkResult::Symlink {
+                    inode: inode.clone(),
+                    symlink: current,
+                };
+            }
+
+            let Some(component) = iter.next() else {
+                break;
+            };
+
+            match (current.inode.load(), &component) {
+                // Normal directory traversal
+                (Some((DentryKind::Directory, _)), _) => {}
+                // Not a directory, fail and exit.
+                (Some(_), _) => return WalkResult::Err(PosixError::ENOTDIR),
+                // Return invalid trailing entries directly.
+                (None, TrailingEmpty) => return WalkResult::Ok(current),
+                // Invalid intermediate entries are not acceptable.
+                (None, _) => return WalkResult::Err(PosixError::ENOENT),
+            }
+
+            match component {
+                PathComponent::TrailingEmpty => {}
+                PathComponent::Current => {}
+                PathComponent::Parent => {
+                    if current.hash_eq(&self.fsroot) {
+                        continue;
+                    }
+
+                    let parent = current.parent().clone();
+                    current = parent;
+                }
+                PathComponent::Name(name) => {
+                    match current.find_full(name).await {
+                        Ok(found) => current = found,
+                        Err(err) => return WalkResult::Err(err),
+                    };
+                }
+            }
+        }
+
+        WalkResult::Ok(current)
+    }
+
+    /// Walk the pathname and get an accurate answer. Stop at symlinks.
+    async fn walk_full(
+        &self,
+        current: ArcRef<'_, Dentry>,
+        iter: &mut PathIterator<'_>,
+    ) -> WalkResult {
+        let (parent_slow, name_slow);
+
+        match self.walk_rcu(current, iter, &rcu_read_lock()) {
+            WalkResultRcu::Err(error) => return WalkResult::Err(error.into()),
+            WalkResultRcu::Ok(dentry) => return WalkResult::Ok(dentry.clone_arc()),
+            WalkResultRcu::Symlink { symlink, inode } => {
+                return WalkResult::Symlink {
+                    symlink: symlink.clone_arc(),
+                    inode,
+                };
+            }
+            WalkResultRcu::Miss { parent, name } => {
+                // Fallback to regular refcounted lookup
+                parent_slow = parent.clone_arc();
+                name_slow = name;
+            }
+        }
+
+        match parent_slow.find_slow(name_slow).await {
+            Ok(found) => self.walk_slow(found, iter).await,
+            Err(err) => return WalkResult::Err(err),
+        }
+    }
+
+    pub async fn follow_symlink(
+        &self,
+        symlink: ArcRef<'_, Dentry>,
+        inode: &InodeUse<dyn Inode>,
+        nr_follows: u32,
+    ) -> KResult<Arc<Dentry>> {
+        let mut target = [0; 256];
+        let mut target = ByteBuffer::new(&mut target);
+        inode.readlink(&mut target).await?;
+
+        self.walk_recursive(
+            &symlink.parent().clone(),
+            Path::new(target.data()).unwrap(),
+            nr_follows + 1,
+        )
+        .await
+    }
+
+    fn follow_symlink_boxed<'r, 'a: 'r, 'b: 'r, 'c: 'r>(
+        &'a self,
+        symlink: ArcRef<'b, Dentry>,
+        inode: &'c InodeUse<dyn Inode>,
+        nr_follows: u32,
+    ) -> Pin<Box<dyn Future<Output = KResult<Arc<Dentry>>> + Send + 'r>> {
+        Box::pin(self.follow_symlink(symlink, inode, nr_follows))
+    }
+
+    async fn walk_recursive(
+        &self,
+        cwd: &Arc<Dentry>,
+        path: &Path,
+        nr_follows: u32,
+    ) -> KResult<Arc<Dentry>> {
+        const MAX_NR_FOLLOWS: u32 = 16;
+
+        let mut current_owned;
+        let mut current;
+        if path.is_absolute() {
+            current = self.fsroot.aref();
+        } else {
+            current = cwd.aref();
+        }
+
+        let mut path_iter = path.iter();
+
+        loop {
+            match self.walk_full(current, &mut path_iter).await {
+                WalkResult::Err(posix_error) => return Err(posix_error.into()),
+                WalkResult::Ok(dentry) => return Ok(dentry),
+                WalkResult::Symlink { symlink, inode } => {
+                    if nr_follows >= MAX_NR_FOLLOWS {
+                        return Err(ELOOP);
+                    }
+
+                    current_owned = self
+                        .follow_symlink_boxed(symlink.aref(), &inode, nr_follows)
+                        .await?;
+                    current = current_owned.aref();
+                }
+            }
+        }
+    }
+
+    pub async fn start_recursive_walk(
+        &self,
+        cwd: &Arc<Dentry>,
+        path: &Path,
+    ) -> KResult<Arc<Dentry>> {
+        self.walk_recursive(cwd, path, 0).await
+    }
+}
+
+impl<'a, 'b> DentryFind<'a, 'b> {
+    fn new(parent: &'a Dentry, name: &'b [u8]) -> Self {
+        let builder: BuildHasherDefault<KernelHasher> = Default::default();
+        let mut hasher = builder.build_hasher();
+
+        hasher.write_usize(parent as *const _ as usize);
+        hasher.write(name);
+        let hash = hasher.finish() as usize;
+
+        Self { parent, name, hash }
+    }
+}
+
+impl DCacheItem for DentryFind<'_, '_> {
+    fn d_hash(&self) -> usize {
+        self.hash
+    }
+
+    fn d_parent(&self) -> *const Dentry {
+        self.parent as *const _
+    }
+
+    fn d_name<'r, 'a: 'r, 'b: 'a>(
+        &'a self,
+        _rcu_read: &'b RCUReadLock,
+    ) -> impl Deref<Target = [u8]> + 'r {
+        self.name
+    }
+}

+ 21 - 19
src/path.rs

@@ -1,34 +1,30 @@
 use crate::{kernel::constants::ENOENT, prelude::*};
 use core::fmt::{self, Debug, Formatter};
 
-pub struct Path<'lt> {
-    all: &'lt [u8],
+#[repr(transparent)]
+pub struct Path {
+    all: [u8],
 }
 
 pub struct PathIterator<'lt> {
     rem: &'lt [u8],
 }
 
-#[allow(dead_code)]
-impl<'lt> Path<'lt> {
-    pub fn new(all: &'lt [u8]) -> KResult<Self> {
+impl Path {
+    pub fn new(all: &[u8]) -> KResult<&Self> {
         if all.is_empty() {
             Err(ENOENT)
         } else {
-            Ok(Self { all })
+            Ok(unsafe { &*(all as *const [u8] as *const Path) })
         }
     }
 
-    pub fn from_str(all: &'lt str) -> KResult<Self> {
-        Self::new(all.as_bytes())
-    }
-
     pub fn is_absolute(&self) -> bool {
         self.all.starts_with(&['/' as u8])
     }
 
-    pub fn iter(&self) -> PathIterator<'lt> {
-        PathIterator::new(self.all)
+    pub fn iter(&self) -> PathIterator {
+        PathIterator::new(&self.all)
     }
 }
 
@@ -46,11 +42,17 @@ pub enum PathComponent<'lt> {
     Parent,
 }
 
+impl PathIterator<'_> {
+    pub fn is_empty(&self) -> bool {
+        self.rem.is_empty()
+    }
+}
+
 impl<'lt> Iterator for PathIterator<'lt> {
     type Item = PathComponent<'lt>;
 
     fn next(&mut self) -> Option<Self::Item> {
-        if self.rem.is_empty() {
+        if self.is_empty() {
             return None;
         }
 
@@ -71,16 +73,16 @@ impl<'lt> Iterator for PathIterator<'lt> {
         self.rem = rem;
 
         match cur {
-            cur if cur.is_empty() => Some(PathComponent::TrailingEmpty),
-            cur if cur == b"." => Some(PathComponent::Current),
-            cur if cur == b".." => Some(PathComponent::Parent),
-            cur => Some(PathComponent::Name(cur)),
+            b"" => Some(PathComponent::TrailingEmpty),
+            b"." => Some(PathComponent::Current),
+            b".." => Some(PathComponent::Parent),
+            name => Some(PathComponent::Name(name)),
         }
     }
 }
 
-impl Debug for Path<'_> {
+impl Debug for Path {
     fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
-        write!(f, "Path({:?})", self.all)
+        write!(f, "Path({:?})", &self.all)
     }
 }

+ 58 - 41
src/rcu.rs

@@ -1,21 +1,35 @@
 use crate::{kernel::task::block_on, prelude::*};
 use alloc::sync::Arc;
+use arcref::ArcRef;
 use core::{
     ops::Deref,
     ptr::NonNull,
     sync::atomic::{AtomicPtr, Ordering},
 };
+use eonix_preempt::PreemptGuard;
 use eonix_runtime::scheduler::RUNTIME;
-use eonix_sync::{Mutex, RwLock, RwLockReadGuard};
+use eonix_sync::{RwLock, RwLockReadGuard};
 use pointers::BorrowedArc;
 
+/// The RCU Read Lock. Holding a reference to an instance of the struct assures
+/// you that any RCU protected data would not be dropped.
+///
+/// The struct cannot be created directly. Instead, use [`rcu_read_lock()`].
+#[derive(Debug)]
+pub struct RCUReadLock();
+
+pub struct RCUReadGuardNew {
+    guard: RwLockReadGuard<'static, RCUReadLock>,
+    _disable_preempt: PreemptGuard<()>,
+}
+
 pub struct RCUReadGuard<'data, T: 'data> {
     value: T,
-    _guard: RwLockReadGuard<'data, ()>,
+    _guard: RwLockReadGuard<'static, RCUReadLock>,
     _phantom: PhantomData<&'data T>,
 }
 
-static GLOBAL_RCU_SEM: RwLock<()> = RwLock::new(());
+static GLOBAL_RCU_SEM: RwLock<RCUReadLock> = RwLock::new(RCUReadLock());
 
 impl<'data, T> RCUReadGuard<'data, BorrowedArc<'data, T>> {
     fn lock(value: BorrowedArc<'data, T>) -> Self {
@@ -25,14 +39,6 @@ impl<'data, T> RCUReadGuard<'data, BorrowedArc<'data, T>> {
             _phantom: PhantomData,
         }
     }
-
-    pub fn borrow(&self) -> BorrowedArc<'data, T> {
-        unsafe {
-            BorrowedArc::from_raw(NonNull::new_unchecked(
-                &raw const *self.value.borrow() as *mut T
-            ))
-        }
-    }
 }
 
 impl<'data, T: 'data> Deref for RCUReadGuard<'data, T> {
@@ -63,17 +69,14 @@ pub trait RCUNode<MySelf> {
 
 pub struct RCUList<T: RCUNode<T>> {
     head: AtomicPtr<T>,
-
-    reader_lock: RwLock<()>,
-    update_lock: Mutex<()>,
+    update_lock: Spin<()>,
 }
 
 impl<T: RCUNode<T>> RCUList<T> {
     pub const fn new() -> Self {
         Self {
             head: AtomicPtr::new(core::ptr::null_mut()),
-            reader_lock: RwLock::new(()),
-            update_lock: Mutex::new(()),
+            update_lock: Spin::new(()),
         }
     }
 
@@ -117,7 +120,6 @@ impl<T: RCUNode<T>> RCUList<T> {
             unsafe { Arc::from_raw(me) };
         }
 
-        let _lck = self.reader_lock.write();
         node.rcu_prev()
             .store(core::ptr::null_mut(), Ordering::Release);
         node.rcu_next()
@@ -152,7 +154,6 @@ impl<T: RCUNode<T>> RCUList<T> {
             unsafe { Arc::from_raw(old) };
         }
 
-        let _lck = self.reader_lock.write();
         old_node
             .rcu_prev()
             .store(core::ptr::null_mut(), Ordering::Release);
@@ -161,36 +162,36 @@ impl<T: RCUNode<T>> RCUList<T> {
             .store(core::ptr::null_mut(), Ordering::Release);
     }
 
-    pub fn iter(&self) -> RCUIterator<T> {
-        let _lck = block_on(self.reader_lock.read());
-
+    pub fn iter<'a, 'r>(&'a self, _lock: &'r RCUReadLock) -> RCUIterator<'a, 'r, T> {
         RCUIterator {
-            // SAFETY: We have a read lock, so the node is still alive.
-            cur: NonNull::new(self.head.load(Ordering::SeqCst)),
-            _lock: _lck,
+            cur: NonNull::new(self.head.load(Ordering::Acquire)),
+            _phantom: PhantomData,
         }
     }
 }
 
-pub struct RCUIterator<'lt, T: RCUNode<T>> {
+pub struct RCUIterator<'list, 'rcu, T: RCUNode<T>> {
     cur: Option<NonNull<T>>,
-    _lock: RwLockReadGuard<'lt, ()>,
+    _phantom: PhantomData<(&'list (), &'rcu ())>,
 }
 
-impl<'lt, T: RCUNode<T>> Iterator for RCUIterator<'lt, T> {
-    type Item = BorrowedArc<'lt, T>;
+impl<'rcu, T: RCUNode<T>> Iterator for RCUIterator<'_, 'rcu, T> {
+    type Item = ArcRef<'rcu, T>;
 
     fn next(&mut self) -> Option<Self::Item> {
-        match self.cur {
-            None => None,
-            Some(pointer) => {
-                // SAFETY: We have a read lock, so the node is still alive.
-                let reference = unsafe { pointer.as_ref() };
+        self.cur.map(|pointer| {
+            let reference = unsafe {
+                // SAFETY: We have the read lock so the node is still alive.
+                pointer.as_ref()
+            };
+
+            self.cur = NonNull::new(reference.rcu_next().load(Ordering::Acquire));
 
-                self.cur = NonNull::new(reference.rcu_next().load(Ordering::SeqCst));
-                Some(unsafe { BorrowedArc::from_raw(pointer) })
+            unsafe {
+                // SAFETY: We have the read lock so the node is still alive.
+                ArcRef::new_unchecked(pointer.as_ptr())
             }
-        }
+        })
     }
 }
 
@@ -228,15 +229,16 @@ where
     }
 
     pub fn load<'lt>(&self) -> Option<RCUReadGuard<'lt, BorrowedArc<'lt, T>>> {
+        // BUG: We should acquire the lock before loading the pointer
         NonNull::new(self.0.load(Ordering::Acquire))
             .map(|p| RCUReadGuard::lock(unsafe { BorrowedArc::from_raw(p) }))
     }
 
-    pub fn load_protected<'a, U: 'a>(
-        &self,
-        _guard: &RCUReadGuard<'a, U>,
-    ) -> Option<BorrowedArc<'a, T>> {
-        NonNull::new(self.0.load(Ordering::Acquire)).map(|p| unsafe { BorrowedArc::from_raw(p) })
+    pub fn dereference<'r, 'a: 'r>(&self, _lock: &'a RCUReadLock) -> Option<ArcRef<'r, T>> {
+        NonNull::new(self.0.load(Ordering::Acquire)).map(|p| unsafe {
+            // SAFETY: We have a read lock, so the node is still alive.
+            ArcRef::new_unchecked(p.as_ptr())
+        })
     }
 
     /// # Safety
@@ -289,3 +291,18 @@ where
         }
     }
 }
+
+impl Deref for RCUReadGuardNew {
+    type Target = RCUReadLock;
+
+    fn deref(&self) -> &Self::Target {
+        &self.guard
+    }
+}
+
+pub fn rcu_read_lock() -> RCUReadGuardNew {
+    RCUReadGuardNew {
+        guard: block_on(GLOBAL_RCU_SEM.read()),
+        _disable_preempt: PreemptGuard::new(()),
+    }
+}