Skip to content

Commit

Permalink
easy-fs: Refactor easy-fs
Browse files Browse the repository at this point in the history
--------

The user of easy-fs can specify the sync primitive used by easy-fs via lock_api::RawMutex.
Add unittests for easy-fs.
Expand the size of the bootstack to 1MiB. After that, the BlockCacheMgr can be successfully initialized.
  • Loading branch information
wyfcyx committed Jan 10, 2024
1 parent e9c42c8 commit 85e2f4d
Show file tree
Hide file tree
Showing 18 changed files with 1,462 additions and 588 deletions.
3 changes: 0 additions & 3 deletions easy-fs-fuse/.gitignore

This file was deleted.

16 changes: 0 additions & 16 deletions easy-fs-fuse/Cargo.toml

This file was deleted.

15 changes: 5 additions & 10 deletions easy-fs/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,18 +1,13 @@
[package]
name = "easy-fs"
version = "0.1.0"
authors = ["Yifan Wu <shinbokuow@163.com>"]
edition = "2018"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
spin = "0.7.0"
lazy_static = { version = "1.4.0", features = ["spin_no_std"] }
lock_api = "0.4.11"

[profile.release]
debug = true

[features]
board_qemu = []
board_k210 = []
[dev-dependencies]
rand = "0.8.5"
clap = "2.33.3"
114 changes: 38 additions & 76 deletions easy-fs-fuse/src/main.rs → easy-fs/examples/fuse.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,39 @@
use clap::{App, Arg};
use easy_fs::{BlockDevice, EasyFileSystem};
use easy_fs::{BlockCacheMgr, BlockDevice, EasyFileSystem, Inode};
use std::fs::{read_dir, File, OpenOptions};
use std::io::{Read, Seek, SeekFrom, Write};
use std::sync::Arc;
use std::sync::Mutex;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex};

const BLOCK_SZ: usize = 512;
const FS_BLOCKS: usize = 32768;
const BLOCK_CACHE_BLKS: usize = 256;
type EasyFileSystemType = EasyFileSystem<BLOCK_CACHE_BLKS, RawSpinlock, RawSpinlock>;

pub struct RawSpinlock(AtomicBool);

unsafe impl lock_api::RawMutex for RawSpinlock {
const INIT: RawSpinlock = RawSpinlock(AtomicBool::new(false));

// A spinlock guard can be sent to another thread and unlocked there
type GuardMarker = lock_api::GuardSend;

fn lock(&self) {
// Note: This isn't the best way of implementing a spinlock, but it
// suffices for the sake of this example.
while !self.try_lock() {}
}

fn try_lock(&self) -> bool {
self.0
.compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed)
.is_ok()
}

unsafe fn unlock(&self) {
self.0.store(false, Ordering::Release);
}
}
struct BlockFile(Mutex<File>);

impl BlockDevice for BlockFile {
Expand Down Expand Up @@ -49,20 +76,22 @@ fn easy_fs_pack() -> std::io::Result<()> {
let src_path = matches.value_of("source").unwrap();
let target_path = matches.value_of("target").unwrap();
println!("src_path = {}\ntarget_path = {}", src_path, target_path);
let block_file = Arc::new(BlockFile(Mutex::new({
let block_file: Arc<dyn BlockDevice> = Arc::new(BlockFile(Mutex::new({
let f = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open(format!("{}{}", target_path, "fs.img"))?;
f.set_len(16 * 2048 * 512).unwrap();
f.set_len((FS_BLOCKS * BLOCK_SZ) as u64).unwrap();
f
})));
let bcache_mgr: BlockCacheMgr<BLOCK_CACHE_BLKS, RawSpinlock> = BlockCacheMgr::new(&block_file);
// 16MiB, at most 4095 files
let efs = EasyFileSystem::create(block_file, 16 * 2048, 1);
let root_inode = Arc::new(EasyFileSystem::root_inode(&efs));
let apps: Vec<_> = read_dir(src_path)
.unwrap()
let efs: EasyFileSystemType = EasyFileSystem::create(FS_BLOCKS as u32, 1, bcache_mgr);
let efs: lock_api::Mutex<RawSpinlock, EasyFileSystemType> = lock_api::Mutex::new(efs);
let efs = Arc::new(efs);
let root_inode = Inode::root_inode(&efs);
let apps: Vec<_> = read_dir(src_path)?
.into_iter()
.map(|dir_entry| {
let mut name_with_ext = dir_entry.unwrap().file_name().into_string().unwrap();
Expand All @@ -80,72 +109,5 @@ fn easy_fs_pack() -> std::io::Result<()> {
// write data to easy-fs
inode.write_at(0, all_data.as_slice());
}
// list apps
// for app in root_inode.ls() {
// println!("{}", app);
// }
Ok(())
}

#[test]
fn efs_test() -> std::io::Result<()> {
let block_file = Arc::new(BlockFile(Mutex::new({
let f = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open("target/fs.img")?;
f.set_len(8192 * 512).unwrap();
f
})));
EasyFileSystem::create(block_file.clone(), 4096, 1);
let efs = EasyFileSystem::open(block_file.clone());
let root_inode = EasyFileSystem::root_inode(&efs);
root_inode.create("filea");
root_inode.create("fileb");
for name in root_inode.ls() {
println!("{}", name);
}
let filea = root_inode.find("filea").unwrap();
let greet_str = "Hello, world!";
filea.write_at(0, greet_str.as_bytes());
//let mut buffer = [0u8; 512];
let mut buffer = [0u8; 233];
let len = filea.read_at(0, &mut buffer);
assert_eq!(greet_str, core::str::from_utf8(&buffer[..len]).unwrap(),);

let mut random_str_test = |len: usize| {
filea.clear();
assert_eq!(filea.read_at(0, &mut buffer), 0,);
let mut str = String::new();
use rand;
// random digit
for _ in 0..len {
str.push(char::from('0' as u8 + rand::random::<u8>() % 10));
}
filea.write_at(0, str.as_bytes());
let mut read_buffer = [0u8; 127];
let mut offset = 0usize;
let mut read_str = String::new();
loop {
let len = filea.read_at(offset, &mut read_buffer);
if len == 0 {
break;
}
offset += len;
read_str.push_str(core::str::from_utf8(&read_buffer[..len]).unwrap());
}
assert_eq!(str, read_str);
};

random_str_test(4 * BLOCK_SZ);
random_str_test(8 * BLOCK_SZ + BLOCK_SZ / 2);
random_str_test(100 * BLOCK_SZ);
random_str_test(70 * BLOCK_SZ + BLOCK_SZ / 7);
random_str_test((12 + 128) * BLOCK_SZ);
random_str_test(400 * BLOCK_SZ);
random_str_test(1000 * BLOCK_SZ);
random_str_test(2000 * BLOCK_SZ);

Ok(())
}
103 changes: 87 additions & 16 deletions easy-fs/src/bitmap.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
use super::{get_block_cache, BlockDevice, BLOCK_SZ};
use alloc::sync::Arc;
use super::{BlockCacheMgr, BLOCK_SZ};

/// A bitmap block
type BitmapBlock = [u64; 64];
/// Number of bits in a block
const BLOCK_BITS: usize = BLOCK_SZ * 8;
/// A bitmap
#[derive(Default)]
pub struct Bitmap {
start_block_id: usize,
blocks: usize,
Expand All @@ -26,14 +27,13 @@ impl Bitmap {
}
}
/// Allocate a new block from a block device
pub fn alloc(&self, block_device: &Arc<dyn BlockDevice>) -> Option<usize> {
pub fn alloc<const N: usize, R>(&self, bcache_mgr: &mut BlockCacheMgr<N, R>) -> Option<usize>
where
R: lock_api::RawMutex,
{
for block_id in 0..self.blocks {
let pos = get_block_cache(
block_id + self.start_block_id as usize,
Arc::clone(block_device),
)
.lock()
.modify(0, |bitmap_block: &mut BitmapBlock| {
let real_block_id = block_id + self.start_block_id as usize;
let pos = bcache_mgr.write_block(real_block_id, 0, |bitmap_block: &mut BitmapBlock| {
if let Some((bits64_pos, inner_pos)) = bitmap_block
.iter()
.enumerate()
Expand All @@ -54,17 +54,88 @@ impl Bitmap {
None
}
/// Deallocate a block
pub fn dealloc(&self, block_device: &Arc<dyn BlockDevice>, bit: usize) {
pub fn dealloc<const N: usize, R>(&self, bcache_mgr: &mut BlockCacheMgr<N, R>, bit: usize)
where
R: lock_api::RawMutex,
{
let (block_pos, bits64_pos, inner_pos) = decomposition(bit);
get_block_cache(block_pos + self.start_block_id, Arc::clone(block_device))
.lock()
.modify(0, |bitmap_block: &mut BitmapBlock| {
assert!(bitmap_block[bits64_pos] & (1u64 << inner_pos) > 0);
bitmap_block[bits64_pos] -= 1u64 << inner_pos;
});
let real_block_id = block_pos + self.start_block_id;
bcache_mgr.write_block(real_block_id, 0, |bitmap_block: &mut BitmapBlock| {
assert!(bitmap_block[bits64_pos] & (1u64 << inner_pos) > 0);
bitmap_block[bits64_pos] -= 1u64 << inner_pos;
});
}
/// Get the max number of allocatable blocks
pub fn maximum(&self) -> usize {
self.blocks * BLOCK_BITS
}
}

#[cfg(test)]
mod tests {
use super::*;
use crate::{test_helper::*, BlockCacheMgr};
use std::sync::Arc;

#[test]
pub fn test_bitmap() {
let block_dev: Arc<dyn BlockDevice> = Arc::new(TestBlockDevice::<16>::new());
let mut bcache_mgr: BlockCacheMgr<4, RawSpinlock> = BlockCacheMgr::new(&block_dev);
let bitmap = Bitmap::new(0, 16);
assert_eq!(bitmap.maximum(), BLOCK_BITS * 16);

let mut buf = [0u8; BLOCK_SZ];
assert_eq!(bitmap.alloc(&mut bcache_mgr), Some(0));
assert_eq!(bitmap.alloc(&mut bcache_mgr), Some(1));
bcache_mgr.sync_all();
block_dev.read_block(0, &mut buf);
assert_eq!(buf[..8], 3u64.to_ne_bytes());
assert!(buf[8..].iter().all(|byte| *byte == 0));

bitmap.dealloc(&mut bcache_mgr, 0);
bcache_mgr.sync_all();
block_dev.read_block(0, &mut buf);
assert_eq!(buf[..8], 2u64.to_ne_bytes());

bitmap.dealloc(&mut bcache_mgr, 1);
bcache_mgr.sync_all();
block_dev.read_block(1, &mut buf);
assert!(buf.iter().all(|byte| *byte == 0));
}

#[test]
#[should_panic]
pub fn test_bitmap_panic() {
let block_dev: Arc<dyn BlockDevice> = Arc::new(TestBlockDevice::<16>::new());
let mut bcache_mgr: BlockCacheMgr<4, RawSpinlock> = BlockCacheMgr::new(&block_dev);
let bitmap = Bitmap::new(0, 1);
assert_eq!(bitmap.alloc(&mut bcache_mgr), Some(0));
bitmap.dealloc(&mut bcache_mgr, 1);
}

#[test]
pub fn test_bitmap_large() {
let block_dev: Arc<dyn BlockDevice> = Arc::new(TestBlockDevice::<16>::new());
let mut bcache_mgr: BlockCacheMgr<4, RawSpinlock> = BlockCacheMgr::new(&block_dev);
let bitmap = Bitmap::new(0, 16);
let mut buf = [0u8; BLOCK_SZ];

for i in 0..16 * BLOCK_BITS {
assert_eq!(bitmap.alloc(&mut bcache_mgr), Some(i));
}
bcache_mgr.sync_all();
for block_id in 0..16usize {
block_dev.read_block(block_id, &mut buf);
assert!(buf.iter().all(|byte| *byte == u8::MAX));
}

for i in 0..16 * BLOCK_BITS {
bitmap.dealloc(&mut bcache_mgr, i);
}
bcache_mgr.sync_all();
for block_id in 0..16usize {
block_dev.read_block(block_id, &mut buf);
assert!(buf.iter().all(|byte| *byte == 0));
}
}
}
Loading

0 comments on commit 85e2f4d

Please sign in to comment.