Skip to content

Commit

Permalink
Merge #636
Browse files Browse the repository at this point in the history
636: Make `SegManager` and `Imap` mutable only through a `Tx` r=jeehoonkang a=travis1829

* `Tx`를 통해서만 `SegManager` 및 `Imap`을 mutable dereference할 수 있도록 수정하였습니다.
(이제, `Lfs`를 통해서는 immutable dereference만 가능합니다.)

* 또한, 이를 위해 `struct Lfs`를 `lfs.rs`라는 새로운 파일로 옮겼습니다.

Co-authored-by: travis1829 <[email protected]>
  • Loading branch information
kaist-cp-bors[bot] and travis1829 authored Aug 28, 2022
2 parents 77b1c78 + 8cc3a10 commit f55cf81
Show file tree
Hide file tree
Showing 8 changed files with 288 additions and 177 deletions.
2 changes: 1 addition & 1 deletion kernel-rs/src/fs/lfs/cleaner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
19 changes: 7 additions & 12 deletions kernel-rs/src/fs/lfs/imap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,15 @@ struct DImapBlock {

impl<'s> From<&'s BufData> for &'s DImapBlock {
fn from(b: &'s BufData) -> Self {
const_assert!(mem::size_of::<DImapBlock>() <= BSIZE);
const_assert!(mem::size_of::<DImapBlock>() <= mem::size_of::<BufData>());
const_assert!(mem::align_of::<BufData>() % mem::align_of::<DImapBlock>() == 0);
unsafe { &*(b.as_ptr() as *const DImapBlock) }
}
}

impl<'s> From<&'s mut BufData> for &'s mut DImapBlock {
fn from(b: &'s mut BufData) -> Self {
const_assert!(mem::size_of::<DImapBlock>() <= BSIZE);
const_assert!(mem::size_of::<DImapBlock>() <= mem::size_of::<BufData>());
const_assert!(mem::align_of::<BufData>() % mem::align_of::<DImapBlock>() == 0);
unsafe { &mut *(b.as_mut_ptr() as *mut DImapBlock) }
}
Expand All @@ -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,
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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::<DImapBlock>() <= mem::size_of::<BufData>());
const_assert!(mem::align_of::<BufData>() % mem::align_of::<DImapBlock>() == 0);
let imap_block: &mut DImapBlock = buf.data_mut().into();
imap_block.entry[offset] = disk_block_no;
buf.free(ctx);
Expand Down
16 changes: 8 additions & 8 deletions kernel-rs/src/fs/lfs/inode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
};
Expand Down Expand Up @@ -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::<Dinode>() <= BSIZE);
const_assert!(mem::size_of::<Dinode>() <= mem::size_of::<BufData>());
const_assert!(mem::align_of::<BufData>() % mem::align_of::<Dinode>() == 0);

// Disk content uses intel byte order.
Expand All @@ -86,15 +86,15 @@ 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::<BufData>());
const_assert!(mem::align_of::<BufData>() % mem::align_of::<[u32; NINDIRECT]>() == 0);
unsafe { &*(b.as_ptr() as *const [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::<BufData>());
const_assert!(mem::align_of::<BufData>() % mem::align_of::<[u32; NINDIRECT]>() == 0);
unsafe { &mut *(b.as_mut_ptr() as *mut [u32; NINDIRECT]) }
}
Expand Down Expand Up @@ -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::<Dinode>() <= mem::size_of::<BufData>());
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -513,8 +513,8 @@ impl Itable<Lfs> {
tx: &Tx<'_, Lfs>,
ctx: &KernelCtx<'_, '_>,
) -> RcInode<Lfs> {
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();
Expand Down
260 changes: 260 additions & 0 deletions kernel-rs/src/fs/lfs/lfs.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,260 @@
//! The `Lfs` struct. Also includes the `Checkpoint`, `SegManagerReadOnlyGuard`, and `ImapReadOnlyGuard` type.
#![allow(clippy::module_inception)]

use core::mem;
use core::ops::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<Superblock>,

/// In-memory inodes.
#[pin]
itable: Itable<Self>,

/// The segment manager.
segmanager: Once<SleepLock<SegManager>>,

/// Imap.
imap: Once<SleepLock<Imap>>,

tx_manager: Once<SleepableLock<TxManager>>,
}

/// 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::<Checkpoint>() <= mem::size_of::<BufData>());
const_assert!(mem::align_of::<BufData>() % mem::align_of::<Checkpoint>() == 0);
unsafe { &*(b.as_ptr() as *const Checkpoint) }
}
}

/// A read-only guard of a `SegManager`.
/// Must be `free`d when done using it.
pub struct SegManagerReadOnlyGuard<'s>(SleepLockGuard<'s, SegManager>);

impl SegManagerReadOnlyGuard<'_> {
pub fn free(self, ctx: &KernelCtx<'_, '_>) {
self.0.free(ctx);
}
}

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.
pub struct ImapReadOnlyGuard<'s>(SleepLockGuard<'s, Imap>);

impl ImapReadOnlyGuard<'_> {
pub fn free(self, ctx: &KernelCtx<'_, '_>) {
self.0.free(ctx);
}
}

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.
/// 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::<Self>::new_itable(),
segmanager: Once::new(),
imap: Once::new(),
tx_manager: Once::new(),
}
}

/// 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<Self>> {
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.
///
/// # 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()
}

/// 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.
///
/// # 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<TxManager> {
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.
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);
}
}
Loading

0 comments on commit f55cf81

Please sign in to comment.