From 576b190d4fdfa172fd8407ae4de015bd0abfa9ec Mon Sep 17 00:00:00 2001 From: travis1829 Date: Tue, 23 Aug 2022 02:26:16 +0900 Subject: [PATCH 1/5] Small fix. --- kernel-rs/src/fs/lfs/imap.rs | 19 +++++++------------ kernel-rs/src/fs/lfs/inode.rs | 8 ++++---- kernel-rs/src/fs/lfs/mod.rs | 4 ++-- kernel-rs/src/fs/lfs/segment.rs | 2 +- kernel-rs/src/fs/lfs/superblock.rs | 4 ++-- 5 files changed, 16 insertions(+), 21 deletions(-) diff --git a/kernel-rs/src/fs/lfs/imap.rs b/kernel-rs/src/fs/lfs/imap.rs index 963ec1a8..907ad711 100644 --- a/kernel-rs/src/fs/lfs/imap.rs +++ b/kernel-rs/src/fs/lfs/imap.rs @@ -23,7 +23,7 @@ struct DImapBlock { impl<'s> From<&'s BufData> for &'s DImapBlock { fn from(b: &'s BufData) -> Self { - const_assert!(mem::size_of::() <= BSIZE); + const_assert!(mem::size_of::() <= mem::size_of::()); const_assert!(mem::align_of::() % mem::align_of::() == 0); unsafe { &*(b.as_ptr() as *const DImapBlock) } } @@ -31,7 +31,7 @@ impl<'s> From<&'s BufData> for &'s DImapBlock { impl<'s> From<&'s mut BufData> for &'s mut DImapBlock { fn from(b: &'s mut BufData) -> Self { - const_assert!(mem::size_of::() <= BSIZE); + const_assert!(mem::size_of::() <= mem::size_of::()); const_assert!(mem::align_of::() % mem::align_of::() == 0); unsafe { &mut *(b.as_mut_ptr() as *mut DImapBlock) } } @@ -40,12 +40,12 @@ impl<'s> From<&'s mut BufData> for &'s mut DImapBlock { /// Stores the address of each imap block. pub struct Imap { dev_no: u32, - ninodes: usize, + ninodes: u32, addr: [u32; IMAPSIZE], } impl Imap { - pub fn new(dev_no: u32, ninodes: usize, addr: [u32; IMAPSIZE]) -> Self { + pub fn new(dev_no: u32, ninodes: u32, addr: [u32; IMAPSIZE]) -> Self { Self { dev_no, ninodes, @@ -91,11 +91,11 @@ impl Imap { let buf = self.get_imap_block(i, ctx); let imap_block: &DImapBlock = buf.data().into(); for j in 0..NENTRY { - let inum = i * NENTRY + j; + let inum = (i * NENTRY + j) as u32; // inum: (0, ninodes) if inum != 0 && inum < self.ninodes && imap_block.entry[j] == 0 { buf.free(ctx); - return Some(inum as u32); + return Some(inum); } } buf.free(ctx); @@ -153,15 +153,10 @@ impl Imap { seg: &mut SegManager, ctx: &KernelCtx<'_, '_>, ) -> bool { - assert!( - 0 < inum && inum < ctx.kernel().fs().superblock().ninodes(), - "invalid inum" - ); + assert!(0 < inum && inum < self.ninodes, "invalid inum"); let (block_no, offset) = self.get_imap_block_no(inum); if let Some(mut buf) = self.update(block_no as u32, seg, ctx) { // Update entry. - const_assert!(mem::size_of::() <= mem::size_of::()); - const_assert!(mem::align_of::() % mem::align_of::() == 0); let imap_block: &mut DImapBlock = buf.data_mut().into(); imap_block.entry[offset] = disk_block_no; buf.free(ctx); diff --git a/kernel-rs/src/fs/lfs/inode.rs b/kernel-rs/src/fs/lfs/inode.rs index 4645655a..04ac7e9b 100644 --- a/kernel-rs/src/fs/lfs/inode.rs +++ b/kernel-rs/src/fs/lfs/inode.rs @@ -10,7 +10,7 @@ use crate::{ fs::{DInodeType, Inode, InodeGuard, InodeType, Itable, RcInode, Tx}, hal::hal, lock::SleepLock, - param::{BSIZE, NINODE, ROOTDEV}, + param::{NINODE, ROOTDEV}, proc::KernelCtx, util::{memset, strong_pin::StrongPin}, }; @@ -70,7 +70,7 @@ impl<'s> TryFrom<&'s BufData> for &'s Dinode { type Error = &'static str; fn try_from(b: &'s BufData) -> Result<&Dinode, &'static str> { - const_assert!(mem::size_of::() <= BSIZE); + const_assert!(mem::size_of::() <= mem::size_of::()); const_assert!(mem::align_of::() % mem::align_of::() == 0); // Disk content uses intel byte order. @@ -86,7 +86,7 @@ impl<'s> TryFrom<&'s BufData> for &'s Dinode { impl<'s> From<&'s BufData> for &'s [u32; NINDIRECT] { fn from(b: &'s BufData) -> Self { - const_assert!(mem::size_of::<[u32; NINDIRECT]>() <= BSIZE); + const_assert!(mem::size_of::<[u32; NINDIRECT]>() <= mem::size_of::()); const_assert!(mem::align_of::() % mem::align_of::<[u32; NINDIRECT]>() == 0); unsafe { &*(b.as_ptr() as *const [u32; NINDIRECT]) } } @@ -94,7 +94,7 @@ impl<'s> From<&'s BufData> for &'s [u32; NINDIRECT] { impl<'s> From<&'s mut BufData> for &'s mut [u32; NINDIRECT] { fn from(b: &'s mut BufData) -> Self { - const_assert!(mem::size_of::<[u32; NINDIRECT]>() <= BSIZE); + const_assert!(mem::size_of::<[u32; NINDIRECT]>() <= mem::size_of::()); const_assert!(mem::align_of::() % mem::align_of::<[u32; NINDIRECT]>() == 0); unsafe { &mut *(b.as_mut_ptr() as *mut [u32; NINDIRECT]) } } diff --git a/kernel-rs/src/fs/lfs/mod.rs b/kernel-rs/src/fs/lfs/mod.rs index bf7d7a8b..d51adbb1 100644 --- a/kernel-rs/src/fs/lfs/mod.rs +++ b/kernel-rs/src/fs/lfs/mod.rs @@ -69,7 +69,7 @@ pub struct Checkpoint { impl<'s> From<&'s BufData> for &'s Checkpoint { fn from(b: &'s BufData) -> Self { - const_assert!(mem::size_of::() <= BSIZE); + const_assert!(mem::size_of::() <= mem::size_of::()); const_assert!(mem::align_of::() % mem::align_of::() == 0); unsafe { &*(b.as_ptr() as *const Checkpoint) } } @@ -177,7 +177,7 @@ impl FileSystem for Lfs { ) }); let _ = self.imap.call_once(|| { - SleepLock::new("imap", Imap::new(dev, superblock.ninodes() as usize, imap)) + SleepLock::new("imap", Imap::new(dev, superblock.ninodes(), imap)) }); let _ = self.tx_manager.call_once(|| { SleepableLock::new( diff --git a/kernel-rs/src/fs/lfs/segment.rs b/kernel-rs/src/fs/lfs/segment.rs index 7cd7f951..26049231 100644 --- a/kernel-rs/src/fs/lfs/segment.rs +++ b/kernel-rs/src/fs/lfs/segment.rs @@ -152,7 +152,7 @@ impl<'s> TryFrom<&'s BufData> for &'s DSegSum { type Error = &'static str; fn try_from(b: &'s BufData) -> Result { - const_assert!(mem::size_of::() <= BSIZE); + const_assert!(mem::size_of::() <= mem::size_of::()); const_assert!(mem::align_of::() % mem::align_of::() == 0); // Disk content uses intel byte order. diff --git a/kernel-rs/src/fs/lfs/superblock.rs b/kernel-rs/src/fs/lfs/superblock.rs index 22195fa7..165fe16d 100644 --- a/kernel-rs/src/fs/lfs/superblock.rs +++ b/kernel-rs/src/fs/lfs/superblock.rs @@ -4,7 +4,7 @@ use static_assertions::const_assert; use crate::{ bio::{Buf, BufData}, - param::{BSIZE, SEGSIZE}, + param::SEGSIZE, }; const FSMAGIC: u32 = 0x10203040; @@ -47,7 +47,7 @@ impl<'s> TryFrom<&'s BufData> for &'s Superblock { type Error = &'static str; fn try_from(b: &'s BufData) -> Result { - const_assert!(mem::size_of::() <= BSIZE); + const_assert!(mem::size_of::() <= mem::size_of::()); const_assert!(mem::align_of::() % mem::align_of::() == 0); // Disk content uses intel byte order. From 4e1b2314ae361bded1a36c002ffbd31153e34b4f Mon Sep 17 00:00:00 2001 From: travis1829 Date: Sat, 27 Aug 2022 16:04:21 +0900 Subject: [PATCH 2/5] Make `SegManager` and `Imap` mutable only through a `Tx`. --- kernel-rs/Cargo.lock | 12 ++++++ kernel-rs/Cargo.toml | 3 +- kernel-rs/src/fs/lfs/cleaner.rs | 2 +- kernel-rs/src/fs/lfs/inode.rs | 8 ++-- kernel-rs/src/fs/lfs/mod.rs | 73 ++++++++++++++++++++++++++++----- kernel-rs/src/fs/lfs/tx.rs | 2 +- 6 files changed, 83 insertions(+), 17 deletions(-) diff --git a/kernel-rs/Cargo.lock b/kernel-rs/Cargo.lock index 52a88f58..d8332878 100644 --- a/kernel-rs/Cargo.lock +++ b/kernel-rs/Cargo.lock @@ -74,6 +74,17 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35" +[[package]] +name = "derive_deref" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcdbcee2d9941369faba772587a565f4f534e42cb8d17e5295871de730163b2b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "either" version = "1.7.0" @@ -185,6 +196,7 @@ dependencies = [ "const-zero", "cortex-a", "cstr_core", + "derive_deref", "itertools", "num-iter", "pin-project", diff --git a/kernel-rs/Cargo.toml b/kernel-rs/Cargo.toml index 6fe3bbc2..c564d5a1 100644 --- a/kernel-rs/Cargo.toml +++ b/kernel-rs/Cargo.toml @@ -13,7 +13,7 @@ default = [] test = [] gicv2 = [] gicv3 = [] -lfs = [] +lfs = ["dep:derive_deref"] [profile.dev] panic = "abort" @@ -31,6 +31,7 @@ bitmaps = { version = "3.2.0", default-features = false } cfg-if = "1.0.0" const-zero = { git = "https://github.com/maxbla/const-zero.git" } cstr_core = { version = "0.2.6", default-features = false } +derive_deref = { version = "1.1.1", optional = true } itertools = { version = "0.10.3", default-features = false } num-iter = { version = "0.1.43", default-features = false } pin-project = "1.0.11" diff --git a/kernel-rs/src/fs/lfs/cleaner.rs b/kernel-rs/src/fs/lfs/cleaner.rs index 8d51026f..e7465eda 100644 --- a/kernel-rs/src/fs/lfs/cleaner.rs +++ b/kernel-rs/src/fs/lfs/cleaner.rs @@ -259,7 +259,7 @@ impl Lfs { inode.free((tx, ctx)); } BlockType::Imap => { - let mut imap = self.imap(ctx); + let mut imap = tx.imap(ctx); imap.update(entry.block_no, seg, ctx).unwrap().free(ctx); if seg.is_full() { seg.commit(true, ctx); diff --git a/kernel-rs/src/fs/lfs/inode.rs b/kernel-rs/src/fs/lfs/inode.rs index 04ac7e9b..0f9c2d27 100644 --- a/kernel-rs/src/fs/lfs/inode.rs +++ b/kernel-rs/src/fs/lfs/inode.rs @@ -228,7 +228,7 @@ impl InodeGuard<'_, Lfs> { /// Copy a modified in-memory inode to disk. pub fn update(&self, tx: &Tx<'_, Lfs>, ctx: &KernelCtx<'_, '_>) { // 1. Write the inode to segment. - let mut seg = tx.fs.segmanager(ctx); + let mut seg = tx.segmanager(ctx); let (mut bp, disk_block_no) = seg.get_or_add_updated_inode_block(self.inum, ctx).unwrap(); const_assert!(mem::size_of::() <= mem::size_of::()); @@ -278,7 +278,7 @@ impl InodeGuard<'_, Lfs> { } // 2. Write the imap to segment. - let mut imap = tx.fs.imap(ctx); + let mut imap = tx.imap(ctx); assert!(imap.set(self.inum, disk_block_no, &mut seg, ctx)); if seg.is_full() { seg.commit(true, ctx); @@ -513,8 +513,8 @@ impl Itable { tx: &Tx<'_, Lfs>, ctx: &KernelCtx<'_, '_>, ) -> RcInode { - let mut seg = tx.fs.segmanager(ctx); - let mut imap = tx.fs.imap(ctx); + let mut seg = tx.segmanager(ctx); + let mut imap = tx.imap(ctx); // 1. Write the inode. let inum = imap.get_empty_inum(ctx).unwrap(); diff --git a/kernel-rs/src/fs/lfs/mod.rs b/kernel-rs/src/fs/lfs/mod.rs index d51adbb1..50dcc132 100644 --- a/kernel-rs/src/fs/lfs/mod.rs +++ b/kernel-rs/src/fs/lfs/mod.rs @@ -2,6 +2,7 @@ use core::cell::UnsafeCell; use core::mem; use core::ops::Deref; +use derive_deref::Deref; use pin_project::pin_project; use spin::Once; use static_assertions::const_assert; @@ -75,6 +76,44 @@ impl<'s> From<&'s BufData> for &'s Checkpoint { } } +/// A read-only guard of a `SegManager`. +/// Must be `free`d when done using it. +#[derive(Deref)] +pub struct SegManagerReadOnlyGuard<'s>(SleepLockGuard<'s, SegManager>); + +impl SegManagerReadOnlyGuard<'_> { + pub fn free(self, ctx: &KernelCtx<'_, '_>) { + self.0.free(ctx); + } +} + +/// A read-only guard of a `SegManager`. +/// Must be `free`d when done using it. +#[derive(Deref)] +pub struct ImapReadOnlyGuard<'s>(SleepLockGuard<'s, Imap>); + +impl ImapReadOnlyGuard<'_> { + pub fn free(self, ctx: &KernelCtx<'_, '_>) { + self.0.free(ctx); + } +} + +impl Tx<'_, Lfs> { + /// Acquires the lock on the `SegManager` and returns the lock guard. + /// Use this to write blocks to the segment. + /// Note that you must `free` the guard when done using it. + pub fn segmanager(&self, ctx: &KernelCtx<'_, '_>) -> SleepLockGuard<'_, SegManager> { + self.fs.segmanager.get().expect("segmanager").lock(ctx) + } + + /// Acquires the lock on the `Imap` and returns the lock guard. + /// Use this to mutate the `Imap`. + /// Note that you must `free` the guard when done using it. + pub fn imap(&self, ctx: &KernelCtx<'_, '_>) -> SleepLockGuard<'_, Imap> { + self.fs.imap.get().expect("imap").lock(ctx) + } +} + impl Lfs { pub const fn new() -> Self { Self { @@ -95,16 +134,30 @@ impl Lfs { unsafe { StrongPin::new_unchecked(&self.as_pin().get_ref().itable) } } - pub fn segmanager(&self, ctx: &KernelCtx<'_, '_>) -> SleepLockGuard<'_, SegManager> { - self.segmanager.get().expect("segmanager").lock(ctx) + /// Acquires the lock on the `SegManager` and returns a read-only guard. + /// Note that you must `free` the guard when done using it. + /// + /// # Note + /// + /// If you need a writable lock guard, use `Tx::segmanager` instead. + /// Note that this means you must have started a transaction. + pub fn segmanager(&self, ctx: &KernelCtx<'_, '_>) -> SegManagerReadOnlyGuard<'_> { + SegManagerReadOnlyGuard(self.segmanager.get().expect("segmanager").lock(ctx)) } pub fn segmanager_raw(&self) -> *mut SegManager { self.segmanager.get().expect("segmanager").get_mut_raw() } - pub fn imap(&self, ctx: &KernelCtx<'_, '_>) -> SleepLockGuard<'_, Imap> { - self.imap.get().expect("imap").lock(ctx) + /// Acquires the lock on the `Imap` and returns a read-only guard. + /// Note that you must `free` the guard when done using it. + /// + /// # Note + /// + /// If you need a writable lock guard, use `Tx::imap` instead. + /// Note that this means you must have started a transaction. + pub fn imap(&self, ctx: &KernelCtx<'_, '_>) -> ImapReadOnlyGuard<'_> { + ImapReadOnlyGuard(self.imap.get().expect("imap").lock(ctx)) } pub fn imap_raw(&self) -> *mut Imap { @@ -176,9 +229,9 @@ impl FileSystem for Lfs { SegManager::new(dev, segtable, superblock.nsegments()), ) }); - let _ = self.imap.call_once(|| { - SleepLock::new("imap", Imap::new(dev, superblock.ninodes(), imap)) - }); + let _ = self + .imap + .call_once(|| SleepLock::new("imap", Imap::new(dev, superblock.ninodes(), imap))); let _ = self.tx_manager.call_once(|| { SleepableLock::new( "tx_manager", @@ -477,7 +530,7 @@ impl FileSystem for Lfs { } let mut tot: u32 = 0; while tot < n { - let mut seg = tx.fs.segmanager(&k); + let mut seg = tx.segmanager(&k); let mut bp = guard.writable_data_block(off as usize / BSIZE, &mut seg, tx, &k); let m = core::cmp::min(n - tot, BSIZE as u32 - off % BSIZE as u32); let begin = (off % BSIZE as u32) as usize; @@ -559,8 +612,8 @@ impl FileSystem for Lfs { // so this acquiresleep() won't block (or deadlock). let mut ip = inode.lock(ctx); - let mut seg = tx.fs.segmanager(ctx); - let mut imap = tx.fs.imap(ctx); + let mut seg = tx.segmanager(ctx); + let mut imap = tx.imap(ctx); assert!(imap.set(ip.inum, 0, &mut seg, ctx)); if seg.is_full() { seg.commit(true, ctx); diff --git a/kernel-rs/src/fs/lfs/tx.rs b/kernel-rs/src/fs/lfs/tx.rs index 7e992853..ff2cf1b9 100644 --- a/kernel-rs/src/fs/lfs/tx.rs +++ b/kernel-rs/src/fs/lfs/tx.rs @@ -80,7 +80,7 @@ impl SleepableLock { seg.free(ctx); if guard.committing || - // This op might exhause the `Bcache`; wait for the outstanding sys calls to be done. + // This op might exhaust the `Bcache`; wait for the outstanding sys calls to be done. (guard.outstanding + 1) * MAXOPBLOCKS as i32 > NBUF as i32 || // This op might exhaust segments; wait for cleaner. (guard.outstanding as u32 + 1) * MAXOPBLOCKS as u32 + MIN_REQUIRED_BLOCKS as u32 > remaining + nfree * (SEGSIZE as u32 - 1) From ddd6963b6effe0d17ad29f2ca01548db67c2a421 Mon Sep 17 00:00:00 2001 From: travis1829 Date: Sat, 27 Aug 2022 16:51:18 +0900 Subject: [PATCH 3/5] Move `Lfs` to another file and make `segmanger`, `imap` invisible. --- kernel-rs/src/fs/lfs/lfs.rs | 215 ++++++++++++++++++++++++++++++++++++ kernel-rs/src/fs/lfs/mod.rs | 207 +--------------------------------- 2 files changed, 220 insertions(+), 202 deletions(-) create mode 100644 kernel-rs/src/fs/lfs/lfs.rs diff --git a/kernel-rs/src/fs/lfs/lfs.rs b/kernel-rs/src/fs/lfs/lfs.rs new file mode 100644 index 00000000..7b767fbe --- /dev/null +++ b/kernel-rs/src/fs/lfs/lfs.rs @@ -0,0 +1,215 @@ +//! The `Lfs` struct. Also includes the `Checkpoint`, `SegManagerReadOnlyGuard`, and `ImapReadOnlyGuard` type. +#![allow(clippy::module_inception)] + +use core::mem; + +use derive_deref::Deref; +use pin_project::pin_project; +use spin::Once; +use static_assertions::const_assert; + +use super::{Imap, Itable, SegManager, SegTable, Superblock, Tx, TxManager}; +use crate::{ + bio::BufData, + hal::hal, + lock::{SleepLock, SleepLockGuard, SleepableLock}, + param::IMAPSIZE, + proc::KernelCtx, + util::strong_pin::StrongPin, +}; + +#[pin_project] +pub struct Lfs { + /// Initializing superblock should run only once because forkret() calls FileSystem::init(). + /// There should be one superblock per disk device, but we run with only one device. + superblock: Once, + + /// In-memory inodes. + #[pin] + itable: Itable, + + /// The segment manager. + segmanager: Once>, + + /// Imap. + imap: Once>, + + tx_manager: Once>, +} + +/// On-disk checkpoint structure. +#[repr(C)] +pub struct Checkpoint { + imap: [u32; IMAPSIZE], + segtable: SegTable, + timestamp: u32, +} + +impl<'s> From<&'s BufData> for &'s Checkpoint { + fn from(b: &'s BufData) -> Self { + const_assert!(mem::size_of::() <= mem::size_of::()); + const_assert!(mem::align_of::() % mem::align_of::() == 0); + unsafe { &*(b.as_ptr() as *const Checkpoint) } + } +} + +/// A read-only guard of a `SegManager`. +/// Must be `free`d when done using it. +#[derive(Deref)] +pub struct SegManagerReadOnlyGuard<'s>(SleepLockGuard<'s, SegManager>); + +impl SegManagerReadOnlyGuard<'_> { + pub fn free(self, ctx: &KernelCtx<'_, '_>) { + self.0.free(ctx); + } +} + +/// A read-only guard of a `SegManager`. +/// Must be `free`d when done using it. +#[derive(Deref)] +pub struct ImapReadOnlyGuard<'s>(SleepLockGuard<'s, Imap>); + +impl ImapReadOnlyGuard<'_> { + pub fn free(self, ctx: &KernelCtx<'_, '_>) { + self.0.free(ctx); + } +} + +impl Tx<'_, Lfs> { + /// Acquires the lock on the `SegManager` and returns the lock guard. + /// Use this to write blocks to the segment. + /// Note that you must `free` the guard when done using it. + pub fn segmanager(&self, ctx: &KernelCtx<'_, '_>) -> SleepLockGuard<'_, SegManager> { + self.fs.segmanager.get().expect("segmanager").lock(ctx) + } + + /// Acquires the lock on the `Imap` and returns the lock guard. + /// Use this to mutate the `Imap`. + /// Note that you must `free` the guard when done using it. + pub fn imap(&self, ctx: &KernelCtx<'_, '_>) -> SleepLockGuard<'_, Imap> { + self.fs.imap.get().expect("imap").lock(ctx) + } +} + +impl Lfs { + pub const fn new() -> Self { + Self { + superblock: Once::new(), + itable: Itable::::new_itable(), + segmanager: Once::new(), + imap: Once::new(), + tx_manager: Once::new(), + } + } + + pub fn superblock(&self) -> &Superblock { + self.superblock.get().expect("superblock") + } + + #[allow(clippy::needless_lifetimes)] + pub fn itable<'s>(self: StrongPin<'s, Self>) -> StrongPin<'s, Itable> { + unsafe { StrongPin::new_unchecked(&self.as_pin().get_ref().itable) } + } + + /// Acquires the lock on the `SegManager` and returns a read-only guard. + /// Note that you must `free` the guard when done using it. + /// + /// # Note + /// + /// If you need a writable lock guard, use `Tx::segmanager` instead. + /// Note that this means you must have started a transaction. + pub fn segmanager(&self, ctx: &KernelCtx<'_, '_>) -> SegManagerReadOnlyGuard<'_> { + SegManagerReadOnlyGuard(self.segmanager.get().expect("segmanager").lock(ctx)) + } + + pub fn segmanager_raw(&self) -> *mut SegManager { + self.segmanager.get().expect("segmanager").get_mut_raw() + } + + /// Acquires the lock on the `Imap` and returns a read-only guard. + /// Note that you must `free` the guard when done using it. + /// + /// # Note + /// + /// If you need a writable lock guard, use `Tx::imap` instead. + /// Note that this means you must have started a transaction. + pub fn imap(&self, ctx: &KernelCtx<'_, '_>) -> ImapReadOnlyGuard<'_> { + ImapReadOnlyGuard(self.imap.get().expect("imap").lock(ctx)) + } + + pub fn imap_raw(&self) -> *mut Imap { + self.imap.get().expect("imap").get_mut_raw() + } + + pub fn tx_manager(&self) -> &SleepableLock { + self.tx_manager.get().expect("tx_manager") + } + + pub fn initialize(&self, dev: u32, ctx: &KernelCtx<'_, '_>) { + if !self.superblock.is_completed() { + // Load the superblock. + let buf = hal().disk().read(dev, 1, ctx); + let superblock = self.superblock.call_once(|| Superblock::new(&buf)); + buf.free(ctx); + + // Load the checkpoint. + let (bno1, bno2) = superblock.get_chkpt_block_no(); + let buf1 = hal().disk().read(dev, bno1, ctx); + let chkpt1: &Checkpoint = buf1.data().into(); + let buf2 = hal().disk().read(dev, bno2, ctx); + let chkpt2: &Checkpoint = buf2.data().into(); + + let (chkpt, timestamp, stored_at_first) = if chkpt1.timestamp > chkpt2.timestamp { + (chkpt1, chkpt1.timestamp, true) + } else { + (chkpt2, chkpt2.timestamp, false) + }; + + let segtable = chkpt.segtable; + let imap = chkpt.imap; + // let timestamp = chkpt.timestamp; + buf1.free(ctx); + buf2.free(ctx); + + // Load other components using the checkpoint content. + let _ = self.segmanager.call_once(|| { + SleepLock::new( + "segment", + SegManager::new(dev, segtable, superblock.nsegments()), + ) + }); + let _ = self + .imap + .call_once(|| SleepLock::new("imap", Imap::new(dev, superblock.ninodes(), imap))); + let _ = self.tx_manager.call_once(|| { + SleepableLock::new( + "tx_manager", + TxManager::new(dev, stored_at_first, timestamp), + ) + }); + } + } + + /// Commits the checkpoint at the checkpoint region. + /// If `first` is `true`, writes it at the first checkpoint region. Otherwise, writes at the second region. + pub fn commit_checkpoint( + &self, + first: bool, + timestamp: u32, + seg: &SegManager, + imap: &Imap, + dev: u32, + ctx: &KernelCtx<'_, '_>, + ) { + let (bno1, bno2) = self.superblock().get_chkpt_block_no(); + let block_no = if first { bno1 } else { bno2 }; + + let mut buf = ctx.kernel().bcache().get_buf_and_clear(dev, block_no, ctx); + let chkpt = unsafe { &mut *(buf.data_mut().as_ptr() as *mut Checkpoint) }; + chkpt.segtable = seg.dsegtable(); + chkpt.imap = imap.dimap(); + chkpt.timestamp = timestamp; + hal().disk().write(&mut buf, ctx); + buf.free(ctx); + } +} diff --git a/kernel-rs/src/fs/lfs/mod.rs b/kernel-rs/src/fs/lfs/mod.rs index 50dcc132..72ebd16a 100644 --- a/kernel-rs/src/fs/lfs/mod.rs +++ b/kernel-rs/src/fs/lfs/mod.rs @@ -2,34 +2,29 @@ use core::cell::UnsafeCell; use core::mem; use core::ops::Deref; -use derive_deref::Deref; -use pin_project::pin_project; -use spin::Once; -use static_assertions::const_assert; - use super::{ DInodeType, FcntlFlags, FileName, FileSystem, Inode, InodeGuard, InodeType, Itable, Path, RcInode, Stat, Tx, }; -use crate::util::strong_pin::StrongPin; use crate::{ - bio::BufData, file::{FileType, InodeFileType}, hal::hal, - lock::{SleepLock, SleepLockGuard, SleepableLock}, - param::{BSIZE, IMAPSIZE}, + param::BSIZE, proc::KernelCtx, + util::strong_pin::StrongPin, }; mod cleaner; mod imap; mod inode; +mod lfs; mod segment; mod superblock; mod tx; use imap::Imap; use inode::{Dinode, Dirent, InodeInner}; +pub use lfs::Lfs; use segment::{SegManager, SegTable}; use superblock::Superblock; use tx::TxManager; @@ -41,204 +36,12 @@ const NDIRECT: usize = 12; const NINDIRECT: usize = BSIZE.wrapping_div(mem::size_of::()); const MAXFILE: usize = NDIRECT.wrapping_add(NINDIRECT); -#[pin_project] -pub struct Lfs { - /// Initializing superblock should run only once because forkret() calls FileSystem::init(). - /// There should be one superblock per disk device, but we run with only one device. - superblock: Once, - - /// In-memory inodes. - #[pin] - itable: Itable, - - /// The segment manager. - segmanager: Once>, - - /// Imap. - imap: Once>, - - tx_manager: Once>, -} - -/// On-disk checkpoint structure. -#[repr(C)] -pub struct Checkpoint { - imap: [u32; IMAPSIZE], - segtable: SegTable, - timestamp: u32, -} - -impl<'s> From<&'s BufData> for &'s Checkpoint { - fn from(b: &'s BufData) -> Self { - const_assert!(mem::size_of::() <= mem::size_of::()); - const_assert!(mem::align_of::() % mem::align_of::() == 0); - unsafe { &*(b.as_ptr() as *const Checkpoint) } - } -} - -/// A read-only guard of a `SegManager`. -/// Must be `free`d when done using it. -#[derive(Deref)] -pub struct SegManagerReadOnlyGuard<'s>(SleepLockGuard<'s, SegManager>); - -impl SegManagerReadOnlyGuard<'_> { - pub fn free(self, ctx: &KernelCtx<'_, '_>) { - self.0.free(ctx); - } -} - -/// A read-only guard of a `SegManager`. -/// Must be `free`d when done using it. -#[derive(Deref)] -pub struct ImapReadOnlyGuard<'s>(SleepLockGuard<'s, Imap>); - -impl ImapReadOnlyGuard<'_> { - pub fn free(self, ctx: &KernelCtx<'_, '_>) { - self.0.free(ctx); - } -} - -impl Tx<'_, Lfs> { - /// Acquires the lock on the `SegManager` and returns the lock guard. - /// Use this to write blocks to the segment. - /// Note that you must `free` the guard when done using it. - pub fn segmanager(&self, ctx: &KernelCtx<'_, '_>) -> SleepLockGuard<'_, SegManager> { - self.fs.segmanager.get().expect("segmanager").lock(ctx) - } - - /// Acquires the lock on the `Imap` and returns the lock guard. - /// Use this to mutate the `Imap`. - /// Note that you must `free` the guard when done using it. - pub fn imap(&self, ctx: &KernelCtx<'_, '_>) -> SleepLockGuard<'_, Imap> { - self.fs.imap.get().expect("imap").lock(ctx) - } -} - -impl Lfs { - pub const fn new() -> Self { - Self { - superblock: Once::new(), - itable: Itable::::new_itable(), - segmanager: Once::new(), - imap: Once::new(), - tx_manager: Once::new(), - } - } - - fn superblock(&self) -> &Superblock { - self.superblock.get().expect("superblock") - } - - #[allow(clippy::needless_lifetimes)] - fn itable<'s>(self: StrongPin<'s, Self>) -> StrongPin<'s, Itable> { - unsafe { StrongPin::new_unchecked(&self.as_pin().get_ref().itable) } - } - - /// Acquires the lock on the `SegManager` and returns a read-only guard. - /// Note that you must `free` the guard when done using it. - /// - /// # Note - /// - /// If you need a writable lock guard, use `Tx::segmanager` instead. - /// Note that this means you must have started a transaction. - pub fn segmanager(&self, ctx: &KernelCtx<'_, '_>) -> SegManagerReadOnlyGuard<'_> { - SegManagerReadOnlyGuard(self.segmanager.get().expect("segmanager").lock(ctx)) - } - - pub fn segmanager_raw(&self) -> *mut SegManager { - self.segmanager.get().expect("segmanager").get_mut_raw() - } - - /// Acquires the lock on the `Imap` and returns a read-only guard. - /// Note that you must `free` the guard when done using it. - /// - /// # Note - /// - /// If you need a writable lock guard, use `Tx::imap` instead. - /// Note that this means you must have started a transaction. - pub fn imap(&self, ctx: &KernelCtx<'_, '_>) -> ImapReadOnlyGuard<'_> { - ImapReadOnlyGuard(self.imap.get().expect("imap").lock(ctx)) - } - - pub fn imap_raw(&self) -> *mut Imap { - self.imap.get().expect("imap").get_mut_raw() - } - - fn tx_manager(&self) -> &SleepableLock { - self.tx_manager.get().expect("tx_manager") - } - - /// Commits the checkpoint at the checkpoint region. - /// If `first` is `true`, writes it at the first checkpoint region. Otherwise, writes at the second region. - pub fn commit_checkpoint( - &self, - first: bool, - timestamp: u32, - seg: &SegManager, - imap: &Imap, - dev: u32, - ctx: &KernelCtx<'_, '_>, - ) { - let (bno1, bno2) = self.superblock().get_chkpt_block_no(); - let block_no = if first { bno1 } else { bno2 }; - - let mut buf = ctx.kernel().bcache().get_buf_and_clear(dev, block_no, ctx); - let chkpt = unsafe { &mut *(buf.data_mut().as_ptr() as *mut Checkpoint) }; - chkpt.segtable = seg.dsegtable(); - chkpt.imap = imap.dimap(); - chkpt.timestamp = timestamp; - hal().disk().write(&mut buf, ctx); - buf.free(ctx); - } -} - impl FileSystem for Lfs { type Dirent = Dirent; type InodeInner = InodeInner; fn init(&self, dev: u32, ctx: &KernelCtx<'_, '_>) { - if !self.superblock.is_completed() { - // Load the superblock. - let buf = hal().disk().read(dev, 1, ctx); - let superblock = self.superblock.call_once(|| Superblock::new(&buf)); - buf.free(ctx); - - // Load the checkpoint. - let (bno1, bno2) = superblock.get_chkpt_block_no(); - let buf1 = hal().disk().read(dev, bno1, ctx); - let chkpt1: &Checkpoint = buf1.data().into(); - let buf2 = hal().disk().read(dev, bno2, ctx); - let chkpt2: &Checkpoint = buf2.data().into(); - - let (chkpt, timestamp, stored_at_first) = if chkpt1.timestamp > chkpt2.timestamp { - (chkpt1, chkpt1.timestamp, true) - } else { - (chkpt2, chkpt2.timestamp, false) - }; - - let segtable = chkpt.segtable; - let imap = chkpt.imap; - // let timestamp = chkpt.timestamp; - buf1.free(ctx); - buf2.free(ctx); - - // Load other components using the checkpoint content. - let _ = self.segmanager.call_once(|| { - SleepLock::new( - "segment", - SegManager::new(dev, segtable, superblock.nsegments()), - ) - }); - let _ = self - .imap - .call_once(|| SleepLock::new("imap", Imap::new(dev, superblock.ninodes(), imap))); - let _ = self.tx_manager.call_once(|| { - SleepableLock::new( - "tx_manager", - TxManager::new(dev, stored_at_first, timestamp), - ) - }); - } + self.initialize(dev, ctx); } fn root(self: StrongPin<'_, Self>) -> RcInode { From 005ff32a01ac11a3337822c90b9ecef59c2244ee Mon Sep 17 00:00:00 2001 From: travis1829 Date: Sat, 27 Aug 2022 16:51:57 +0900 Subject: [PATCH 4/5] Add documentation. --- kernel-rs/src/fs/lfs/lfs.rs | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/kernel-rs/src/fs/lfs/lfs.rs b/kernel-rs/src/fs/lfs/lfs.rs index 7b767fbe..d1cab8e1 100644 --- a/kernel-rs/src/fs/lfs/lfs.rs +++ b/kernel-rs/src/fs/lfs/lfs.rs @@ -102,11 +102,17 @@ impl Lfs { } } + /// Returns a reference to the `Superblock`. + /// + /// # Panic + /// + /// Panics if `self` is not initialized. pub fn superblock(&self) -> &Superblock { self.superblock.get().expect("superblock") } #[allow(clippy::needless_lifetimes)] + /// Returns a reference to the `Itable`. pub fn itable<'s>(self: StrongPin<'s, Self>) -> StrongPin<'s, Itable> { unsafe { StrongPin::new_unchecked(&self.as_pin().get_ref().itable) } } @@ -118,10 +124,19 @@ impl Lfs { /// /// If you need a writable lock guard, use `Tx::segmanager` instead. /// Note that this means you must have started a transaction. + /// + /// # Panic + /// + /// Panics if `self` is not initialized. pub fn segmanager(&self, ctx: &KernelCtx<'_, '_>) -> SegManagerReadOnlyGuard<'_> { SegManagerReadOnlyGuard(self.segmanager.get().expect("segmanager").lock(ctx)) } + /// Returns a raw pointer to the `SegManager`. + /// + /// # Panic + /// + /// Panics if `self` is not initialized. pub fn segmanager_raw(&self) -> *mut SegManager { self.segmanager.get().expect("segmanager").get_mut_raw() } @@ -133,18 +148,34 @@ impl Lfs { /// /// If you need a writable lock guard, use `Tx::imap` instead. /// Note that this means you must have started a transaction. + /// + /// # Panic + /// + /// Panics if `self` is not initialized. pub fn imap(&self, ctx: &KernelCtx<'_, '_>) -> ImapReadOnlyGuard<'_> { ImapReadOnlyGuard(self.imap.get().expect("imap").lock(ctx)) } + /// Returns a raw pointer to the `Imap`. + /// + /// # Panic + /// + /// Panics if `self` is not initialized. pub fn imap_raw(&self) -> *mut Imap { self.imap.get().expect("imap").get_mut_raw() } + /// Acquires the lock on the `TxManager` and returns a lock guard. + /// + /// # Panic + /// + /// Panics if `self` is not initialized. pub fn tx_manager(&self) -> &SleepableLock { self.tx_manager.get().expect("tx_manager") } + /// Initializes `self`. + /// Does nothing if already initialized. pub fn initialize(&self, dev: u32, ctx: &KernelCtx<'_, '_>) { if !self.superblock.is_completed() { // Load the superblock. From 8cc3a10f8ee50f9e6d3cdede1df990068935346a Mon Sep 17 00:00:00 2001 From: travis1829 Date: Sun, 28 Aug 2022 00:04:53 +0900 Subject: [PATCH 5/5] Manually implement `Deref` instead of using `derive`. --- kernel-rs/Cargo.lock | 12 ------------ kernel-rs/Cargo.toml | 3 +-- kernel-rs/src/fs/lfs/lfs.rs | 20 +++++++++++++++++--- 3 files changed, 18 insertions(+), 17 deletions(-) diff --git a/kernel-rs/Cargo.lock b/kernel-rs/Cargo.lock index d8332878..52a88f58 100644 --- a/kernel-rs/Cargo.lock +++ b/kernel-rs/Cargo.lock @@ -74,17 +74,6 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35" -[[package]] -name = "derive_deref" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcdbcee2d9941369faba772587a565f4f534e42cb8d17e5295871de730163b2b" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "either" version = "1.7.0" @@ -196,7 +185,6 @@ dependencies = [ "const-zero", "cortex-a", "cstr_core", - "derive_deref", "itertools", "num-iter", "pin-project", diff --git a/kernel-rs/Cargo.toml b/kernel-rs/Cargo.toml index c564d5a1..6fe3bbc2 100644 --- a/kernel-rs/Cargo.toml +++ b/kernel-rs/Cargo.toml @@ -13,7 +13,7 @@ default = [] test = [] gicv2 = [] gicv3 = [] -lfs = ["dep:derive_deref"] +lfs = [] [profile.dev] panic = "abort" @@ -31,7 +31,6 @@ bitmaps = { version = "3.2.0", default-features = false } cfg-if = "1.0.0" const-zero = { git = "https://github.com/maxbla/const-zero.git" } cstr_core = { version = "0.2.6", default-features = false } -derive_deref = { version = "1.1.1", optional = true } itertools = { version = "0.10.3", default-features = false } num-iter = { version = "0.1.43", default-features = false } pin-project = "1.0.11" diff --git a/kernel-rs/src/fs/lfs/lfs.rs b/kernel-rs/src/fs/lfs/lfs.rs index d1cab8e1..c31dd3fa 100644 --- a/kernel-rs/src/fs/lfs/lfs.rs +++ b/kernel-rs/src/fs/lfs/lfs.rs @@ -2,8 +2,8 @@ #![allow(clippy::module_inception)] use core::mem; +use core::ops::Deref; -use derive_deref::Deref; use pin_project::pin_project; use spin::Once; use static_assertions::const_assert; @@ -55,7 +55,6 @@ impl<'s> From<&'s BufData> for &'s Checkpoint { /// A read-only guard of a `SegManager`. /// Must be `free`d when done using it. -#[derive(Deref)] pub struct SegManagerReadOnlyGuard<'s>(SleepLockGuard<'s, SegManager>); impl SegManagerReadOnlyGuard<'_> { @@ -64,9 +63,16 @@ impl SegManagerReadOnlyGuard<'_> { } } +impl Deref for SegManagerReadOnlyGuard<'_> { + type Target = SegManager; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + /// A read-only guard of a `SegManager`. /// Must be `free`d when done using it. -#[derive(Deref)] pub struct ImapReadOnlyGuard<'s>(SleepLockGuard<'s, Imap>); impl ImapReadOnlyGuard<'_> { @@ -75,6 +81,14 @@ impl ImapReadOnlyGuard<'_> { } } +impl Deref for ImapReadOnlyGuard<'_> { + type Target = Imap; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + impl Tx<'_, Lfs> { /// Acquires the lock on the `SegManager` and returns the lock guard. /// Use this to write blocks to the segment.